home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 6984 / 6984.xpi / chrome / lazarus.jar / content / lazarus.js < prev    next >
Text File  |  2009-11-24  |  156KB  |  4,418 lines

  1.  
  2. //declare namespace
  3. this.Lazarus = this.Lazarus || {}
  4.  
  5. Lazarus.STATE_UNINITALIZED = 0; 
  6. Lazarus.STATE_DISABLED = 1;
  7. Lazarus.STATE_PASSWORD_REQUIRED = 2;
  8. Lazarus.STATE_ENABLED = 3;
  9. Lazarus.STATE_DISABLED_FOR_DOMAIN = 4;
  10. Lazarus.STATE_PRIVATE_BROWSING = 5;
  11. Lazarus.STATE_GENERATING_KEYS = 6;
  12.  
  13.  
  14. Lazarus.LOGIN_HOSTNAME = 'chrome://lazarus';
  15. Lazarus.LOGIN_REALM = 'Private Key Password';
  16. Lazarus.LOGIN_USERNAME = 'lazarus-private-key';
  17.  
  18. Lazarus.IFRAME_NAME = '<content-editable-iframes>';
  19. Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION = 1024; //characters
  20.  
  21. //flag to indicate if this browser is ready yet.
  22. Lazarus.initalized = false;
  23.  
  24. //timers
  25. Lazarus.cleanupSavedFormsTimer = 0;
  26. Lazarus.autoSaveFormTimer = 0;
  27.  
  28. //pointer to last autosave form.
  29. Lazarus.currAutoSaveForm = null;
  30.  
  31. //pointer to the last editor (txetbox or iframe) that a user put input into.
  32. Lazarus.currentEditor = null;
  33.  
  34. //flag to say if context menu is currently being shown
  35. Lazarus.isContextMenuShowing = false;
  36.  
  37. /* known input types
  38. case "text":
  39. case "textarea":
  40. case "file":
  41. case "radio":
  42. case "checkbox":
  43. case "select":
  44. case "password":
  45. case "hidden":
  46. case "submit":
  47. case "reset":
  48. case "button":
  49. case "image":
  50. */
  51.  
  52.  
  53.  
  54. //array of editor infos that the user has typed into this session 
  55. Lazarus.editorInfos = [];
  56.  
  57.  
  58. /**
  59. * window has loaded
  60. */
  61. Lazarus.init = function(){
  62.     
  63.     Lazarus.Global  = Components.utils.import("resource://lazarus/global.js").Global;
  64.     Lazarus.Crypto = Components.utils.import("resource://lazarus/crypto.js").Crypto;
  65.     
  66.     Lazarus.initalized = true;
  67.     Lazarus.initDevEnviroment();
  68.     
  69.     //set loading icon
  70.     Lazarus.refreshIcon();
  71.     Lazarus.repositionNotification();
  72.     
  73.     //Update UI elements 
  74.     Lazarus.$("lazarus-statusbaricon-tooltip-title").setAttribute("value", Lazarus.getString("lazarus.statusbarpanel.image.tooltip", Lazarus.getVersionStr() +" ["+ Lazarus.build +"]")); 
  75.     Lazarus.refreshMenuIcons();
  76.     Lazarus.Pref.addObserver('extensions.lazarus.showContextMenuIcons', Lazarus.refreshMenuIcons);
  77.     
  78.     if (Lazarus.getPref('extensions.lazarus.showDonateNotification')){
  79.       Lazarus.checkForDonateThanks();
  80.     }
  81.     
  82.     
  83.     //check crypto component (breaks a lot with various Linux builds)
  84.     if (Lazarus.checkCrypto()){      
  85.       //open the database
  86.       if (Lazarus.initDB()){
  87.         if (Lazarus.initEncryptionKeys()){
  88.           if (Lazarus.loadPublicKey()){
  89.               Lazarus.enable();
  90.           }
  91.             
  92.           Lazarus.refreshIcon();  
  93.  
  94.           Lazarus.saveAutoSaveText();
  95.           
  96.           //remove expired forms
  97.           Lazarus.cleanupSavedForms();
  98.           
  99.           //hmmm, interesting. Cleaning the database makes little difference to the database size (33MB -> 32MB), 
  100.           // unless a lot of deletions have been made 
  101.           //
  102.           if (Lazarus.getPref('extensions.lazarus.cleanDatabaseAtStartup')){
  103.             Lazarus.cleanDB();
  104.           }        
  105.         }
  106.         //else we are generating new encryption keys, do nothing
  107.       }
  108.       else {
  109.         //errors *should* have been thrown in the error console.
  110.         //inform the user something went wrong :(
  111.         Lazarus.error("Failed to load database");
  112.         Lazarus.disable();
  113.       }
  114.     }
  115.     else {
  116.       Lazarus.error("Failed to load crypto component");
  117.       Lazarus.disable();
  118.     }
  119.     Lazarus.refreshIcon();  
  120. }
  121.  
  122. /**
  123. * turn off the donate notifications if this user has donated.
  124. */
  125. Lazarus.checkForDonateThanks = function(){
  126.  
  127.   var onload = function(uri){
  128.     if (uri && uri.spec && uri.spec.indexOf('//lazarus.interclue.com/donate-thanks.html') > -1){
  129.       Lazarus.setPref('extensions.lazarus.showDonateNotification', false);
  130.       Lazarus.Event.remove("location-change", onload);
  131.     }
  132.   }
  133.   Lazarus.Event.add("location-change", onload);
  134. }
  135.  
  136. /**
  137. * check to make sure the crypto component has loaded correctly
  138. */
  139. Lazarus.checkCrypto = function(){
  140.   try {
  141.     var crypto = Components.classes["@labs.mozilla.com/Weave/Crypto;1"].createInstance(Components.interfaces.IWeaveCrypto);
  142.     return true;
  143.   }
  144.   catch(e){
  145.     return false;
  146.   }
  147. }
  148.  
  149. /**
  150. * reposition the notification bar so that it's always the last element 
  151. */
  152. Lazarus.repositionNotification = function(){
  153.     //#124: Zotero compatiilty problem
  154.     var notif = document.getElementById('lazarus-notification');
  155.     notif.parentNode.appendChild(notif);
  156. }
  157.  
  158.  
  159.  
  160. /**
  161. * enables Lazarus for this browser
  162. */
  163. Lazarus.enable = function(){
  164.     
  165.     Lazarus.saveAutoSavedForms(); 
  166.     
  167.     //add preference observers
  168.     Lazarus.Pref.addObserver("extensions.lazarus.expireSavedForms", Lazarus.startCleanupTimer);
  169.     Lazarus.Pref.addObserver("extensions.lazarus.expireSavedFormsInterval", Lazarus.startCleanupTimer);
  170.     Lazarus.Pref.addObserver("extensions.lazarus.showInStatusbar", Lazarus.refreshIcon);
  171.     
  172.     //we need to capture any onsubmit event from forms within a webpage
  173.     gBrowser.addEventListener("submit", Lazarus.onFormSubmit, false);
  174.     gBrowser.addEventListener("submit", Lazarus.saveLastSubmittedForm, false);
  175.     gBrowser.addEventListener("DOMContentLoaded", Lazarus.autofillEvent, true);
  176.     gBrowser.addEventListener("DOMContentLoaded", Lazarus.initRecoverForm, true);
  177.     gBrowser.addEventListener("reset", Lazarus.onFormReset, false);
  178.     gBrowser.addEventListener("change", Lazarus.onFormChange, false);
  179.     //we also need to save forms if people are typing into them.
  180.     gBrowser.addEventListener("keyup", Lazarus.onKeyUp, false);
  181.     
  182.     //kjd: removing lazarus icon for now
  183.     //gBrowser.addEventListener("keydown", Lazarus.onKeyDown, false);
  184.     
  185.         
  186.     //clear the saved forms if user wants to when "clear private data" is hit.
  187.     Lazarus.$("Tools:Sanitize").addEventListener("command", Lazarus.fireClearPrivateDataIfNoPrompt, false);
  188.     
  189.     //add handlers to the context menu
  190.     Lazarus.$("contentAreaContextMenu").addEventListener("popupshowing", Lazarus.onContextMenuShowing, false);
  191.     Lazarus.$("contentAreaContextMenu").addEventListener("popuphidden", Lazarus.onContextMenuHide, false);
  192.     
  193.     //need events when a user changes the current document
  194.     Lazarus.Event.add("location-change", Lazarus.onLocationChange);    
  195.     
  196.     Lazarus.Event.add("extension-uninstall", Lazarus.onUninstall);
  197.     Lazarus.Event.add("extension-uninstall-request", Lazarus.onUninstallRequest);
  198.     Lazarus.Event.add("application-startup", Lazarus.onStartUp);
  199.     Lazarus.Event.add("application-startup", Lazarus.removeOldForms);
  200.     Lazarus.Event.add("application-shutdown", Lazarus.fireClearPrivateDataOnShutdown);
  201.     Lazarus.Event.add("application-shutdown", Lazarus.onShutdown);
  202.     Lazarus.Event.add("clear-private-data", Lazarus.onClearPrivateData);
  203.         
  204.     Lazarus.startCleanupTimer();
  205.         
  206.     //show the statusbar image (if user wants it)
  207.     Lazarus.refreshIcon();    
  208. }
  209.  
  210.  
  211. Lazarus.onKeyDown = function(evt){
  212.   var ele = evt.target;
  213.     
  214.   //ignore if we have already attached an onblur handler to this element
  215.   if (!ele.lazarusIconAdded){
  216.     switch(Lazarus.getElementType(ele)){
  217.       case "text":
  218.       case "password":
  219.       case "textarea":
  220.         //are we saving this element?
  221.         var form = Lazarus.findFormFromElement(ele);
  222.         var editor = Lazarus.findEditorFromElement(ele);
  223.         if ((form && Lazarus.shouldSaveForm(form)) || (editor && Lazarus.shouldSaveEditorInfo(form))){
  224.           //highlight element
  225.           Lazarus.addBackgroundIcon(ele);
  226.         }
  227.         else {
  228.           //ignore furthur keypresses on this element?
  229.         }
  230.         
  231.       default:
  232.         //ignore this element
  233.     }
  234.   }
  235. }
  236.  
  237.  
  238. Lazarus.addBackgroundIcon = function(ele){
  239.  
  240.   if (!ele.lazarusIconAdded){
  241.     ele.lazarusIconAdded = true;
  242.  
  243.     var doc = ele.ownerDocument;
  244.     if (!doc.lazarusIcon){
  245.       var div = doc.createElement('div');
  246.       div.style.width = "11px";
  247.       div.style.height = "14px";
  248.       div.style.position = "absolute";
  249.       div.style.background = "url("+ Lazarus.icon +") no-repeat center center";
  250.       div.title = Lazarus.getString('Lazarus.savingText'); 
  251.       doc.body.appendChild(div);
  252.       doc.lazarusIcon = div;
  253.     }
  254.     //now position the image over the end of the textbox
  255.     var rect = ele.getBoundingClientRect();
  256.     doc.lazarusIcon.style.top = rect.top +"px";
  257.     doc.lazarusIcon.style.left = (rect.right - parseInt(doc.lazarusIcon.style.width)) +"px";
  258.     doc.lazarusIcon.style.display = "block";
  259.     
  260.     var onBlur = function(){
  261.       //hide the icon
  262.       doc.lazarusIcon.style.display = "none";
  263.       ele.lazarusIconAdded = false;
  264.     }
  265.     
  266.     //when adding the icon, we should hide it on blur
  267.     ele.addEventListener("blur", onBlur, false);
  268.   }
  269. }
  270.  
  271.  
  272. /**
  273. * restart the browser
  274. */
  275. Lazarus.restart = function(){
  276.   var nsIAppStartup = Components.interfaces.nsIAppStartup;
  277.   Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(nsIAppStartup).quit(nsIAppStartup.eRestart | nsIAppStartup.eAttemptQuit);
  278. }
  279.  
  280. /**
  281. * turns Lazarus off for this browser
  282. */
  283. Lazarus.disable = function(){
  284.     
  285.     //add preference observers
  286.     Lazarus.Pref.addObserver("extensions.lazarus.expireSavedForms", Lazarus.startCleanupTimer);
  287.     Lazarus.Pref.addObserver("extensions.lazarus.expireSavedFormsInterval", Lazarus.startCleanupTimer);
  288.     Lazarus.Pref.addObserver("extensions.lazarus.showInStatusbar", Lazarus.refreshIcon);
  289.     
  290.     //we need to capture any onsubmit event from forms within a webpage
  291.     gBrowser.removeEventListener("submit", Lazarus.onFormSubmit, false);
  292.     gBrowser.removeEventListener("submit", Lazarus.saveLastSubmittedForm, false);
  293.     gBrowser.removeEventListener("DOMContentLoaded", Lazarus.autofillEvent, true);
  294.     gBrowser.removeEventListener("DOMContentLoaded", Lazarus.initRecoverForm, true);
  295.     gBrowser.removeEventListener("reset", Lazarus.onFormReset, false);
  296.     gBrowser.removeEventListener("change", Lazarus.onFormChange, false);
  297.     //we also need to save forms if people are typing into them.
  298.     gBrowser.removeEventListener("keyup", Lazarus.onKeyUp, false);
  299.     
  300.     //need events when a user changes the current document
  301.     Lazarus.Event.remove("location-change", Lazarus.onLocationChange);
  302.         
  303.     //clear the saved forms if user wants to when "clear private data" is hit.
  304.     Lazarus.$("Tools:Sanitize").removeEventListener("command", Lazarus.fireClearPrivateDataIfNoPrompt, false);
  305.         
  306.     Lazarus.stopCleanupTimer();
  307.     
  308.     //and close the database
  309.     Lazarus.db.close();
  310.     
  311.     //update the statusbar image 
  312.     Lazarus.refreshIcon();
  313. }
  314.  
  315. /**
  316. * return TRUE if we can encrypt a string (ie the Crypto component is working, and the public key exists)
  317. */
  318. Lazarus.canEncrypt = function(){
  319.     return Lazarus.Crypto.publicKey ? true : false;
  320. }
  321.  
  322. Lazarus.canDecrypt = function(){
  323.     return Lazarus.Crypto.privateKey ? true : false;
  324. }
  325.  
  326.  
  327.  
  328. /**
  329. * initalizes the lazarus recover-form page
  330. */
  331. Lazarus.initRecoverForm = function(evt){
  332.     var doc = evt.originalTarget;
  333.     if (Lazarus.isDocRecoveryForm(doc)){
  334.         doc.title = Lazarus.getString("recoverform.title");
  335.         
  336.         //Lazarus.$('heading', doc).innerHTML = Lazarus.getString("recoverform.title");
  337.         Lazarus.$('description', doc).innerHTML = Lazarus.getString("recoverform.description");
  338.         Lazarus.$('notes', doc).innerHTML= Lazarus.getString("recoverform.notes");
  339.         
  340.         Lazarus.$('form-url-label', doc).innerHTML= Lazarus.getString("recoverform.form.url");
  341.         Lazarus.$('form-action-label', doc).innerHTML= Lazarus.getString("recoverform.form.action");
  342.         Lazarus.$('notes', doc).innerHTML= Lazarus.getString("recoverform.notes");
  343.         
  344.         var m = doc.URL.match(/[\?&]id=(\d+)/)
  345.         var id = m ? parseInt(m[1]) : -1;
  346.         if (id > -1){
  347.             var row = Lazarus.db.getRow("SELECT * FROM forms WHERE id = ?1", id);        
  348.             if (row){
  349.                 if (Lazarus.canDecrypt()){
  350.                     var formInfo = Lazarus.JSON.decode(Lazarus.decrypt(row["forminfo"]));
  351.                     
  352.                     Lazarus.$('form-url', doc).innerHTML = formInfo.origURL ? Lazarus.generateLinkFromURL(formInfo.origURL) : '';
  353.                     Lazarus.$('form-action', doc).innerHTML = formInfo.action ? Lazarus.generateLinkFromURL(formInfo.action) : '';
  354.                     
  355.                     var form = Lazarus.buildForm(formInfo, doc);
  356.                     form.setAttribute("lazarus-form-id", row["formid"]);
  357.                     Lazarus.$('form-box', doc).appendChild(form);
  358.                     Lazarus.restoreForm(form, formInfo);
  359.                 }
  360.                 else {
  361.                     //need to explain to user why we cannot fill in the form 
  362.                     Lazarus.showNotificationBox("password-required");
  363.                 }
  364.             }
  365.             else {
  366.                 Lazarus.$('form-box', doc).innerHTML = Lazarus.getString("error.form.not.found");
  367.                 Lazarus.$('form-box', doc).className = "warning";
  368.             }
  369.         }
  370.         else {
  371.             Lazarus.$('form-box', doc).innerHTML = Lazarus.getString("error.form.not.found");
  372.             Lazarus.$('form-box', doc).className = "warning";
  373.         }
  374.     }
  375. }
  376.  
  377. /**
  378. * return TRUE if the given document is the lazarus Recover Form page.
  379. */
  380. Lazarus.isDocRecoveryForm = function(doc){
  381.     return (doc && doc.URL && doc.URL.indexOf("chrome://lazarus/content/recover-form.html") == 0);
  382. }
  383.  
  384. /**
  385. * generate an HTML link given a url.
  386. * if url is too long, then truncate url to maxChars
  387. */
  388. Lazarus.generateLinkFromURL = function(url, maxChars){
  389.     
  390.     maxChars = maxChars || 50;
  391.     
  392.     if (/^(file|http|https):/.test(url)){
  393.         var text = url;
  394.         if (text.length > maxChars){
  395.             text = text.substring(0, maxChars -3) +"...";
  396.         }
  397.         
  398.         return '<a href="'+ Lazarus.htmlEncode(url) +'" title="'+ Lazarus.htmlEncode(url) +'">'+ Lazarus.htmlEncode(text) +'</a>';
  399.     }
  400.     else {
  401.         return Lazarus.htmlEncode(url);
  402.     }
  403. }
  404.  
  405. /**
  406. * encode a string for display in an html page
  407. */
  408. Lazarus.htmlEncode = function(str){
  409.     str = str.replace(/&/g, "&");
  410.     str = str.replace(/</g, "<");
  411.     str = str.replace(/>/g, ">");
  412.     str = str.replace(/"/g, """);
  413.     return str;
  414. }
  415.  
  416. /**
  417. * builds an HTML form from a formInfo object
  418. */
  419. Lazarus.buildForm = function(formInfo, doc){
  420.     var form = doc.createElement("form");
  421.     form.setAttribute("method", formInfo.method || "get");
  422.     form.setAttribute("enctype", formInfo.enctype || "");
  423.     //support for AJAX textareas
  424.     form.isTextarea = formInfo.isTextarea;
  425.     
  426.     for(var name in formInfo.fields){
  427.         for (var i=0; i<formInfo.fields[name].length; i++){
  428.             var fieldInfo = formInfo.fields[name][i];
  429.             var ele = null;
  430.             var eleLabel = '';
  431.             switch(fieldInfo.type){
  432.                 case "radio":
  433.                 case "checkbox":
  434.                     if (formInfo.version && formInfo.version >= 1 && fieldInfo.value && typeof fieldInfo.value.valueAttr != "undefined"){
  435.                         ele = doc.createElement("input");
  436.                         ele.setAttribute("type", fieldInfo.type);
  437.                         ele.setAttribute("value", fieldInfo.value.valueAttr);
  438.                         eleLabel = name +"["+ fieldInfo.value.valueAttr +"]";
  439.                     }
  440.                     break;
  441.                 
  442.                 case "password":
  443.                 case "hidden":
  444.                 case "file":
  445.                 case "text":
  446.                     ele = doc.createElement("input");
  447.                     ele.setAttribute("type", fieldInfo.type);
  448.                     break;
  449.  
  450.                 case "textarea":
  451.                     ele = doc.createElement("textarea");
  452.                     break;
  453.  
  454.                 case "select":
  455.                     if (formInfo.version && formInfo.version >= 1 && Lazarus.isArray(fieldInfo.value) && fieldInfo.value.length){
  456.                         ele = doc.createElement("select");
  457.                     
  458.                         for (var i=0; i<fieldInfo.value.length; i++){
  459.                             var opt = doc.createElement("option");
  460.                             opt.setAttribute("value", fieldInfo.value[i]);
  461.                             opt.appendChild(doc.createTextNode(fieldInfo.value[i]));
  462.                             ele.appendChild(opt);
  463.                         }
  464.                         if (fieldInfo.value.length > 1){
  465.                             ele.setAttribute("size", fieldInfo.value.length);
  466.                             ele.setAttribute("multiple", "true");
  467.                         }
  468.                     }
  469.                     break;
  470.                 
  471.                 //no saved
  472.                 case "submit":
  473.                 case "reset":
  474.                 case "button":
  475.                 case "image":
  476.                     break;
  477.                     
  478.                 case "iframe":
  479.                     //ignore iframes for now?
  480.                     break;
  481.                     
  482.                 default:
  483.                     Lazarus.error("Unknown element type ["+ fieldInfo.type +"]");
  484.             }       
  485.             
  486.             if (ele){
  487.                 if (fieldInfo.type == "hidden"){
  488.                     form.appendChild(ele);
  489.                 }
  490.                 else {
  491.                     var box = doc.createElement("div");
  492.                     box.setAttribute("class", "form-field-box");
  493.                     
  494.                     var label = doc.createElement("label");
  495.                     var text = doc.createTextNode(eleLabel || name);
  496.                     label.appendChild(text);
  497.                     box.appendChild(label);
  498.                 
  499.                     ele.setAttribute("name", fieldInfo.name);
  500.                     ele.setAttribute("class", "form-field "+ fieldInfo.type);
  501.                     box.appendChild(ele);
  502.                     form.appendChild(box);
  503.                 }
  504.             }
  505.         }
  506.     }
  507.     
  508.     return form;
  509. }
  510.  
  511. /**
  512. * save the last submitted form id for use in auto restore template
  513. */
  514. Lazarus.saveLastSubmittedForm = function(evt){
  515.     var form = Lazarus.findFormFromElement(evt.target);
  516.     if (form){
  517.         Lazarus.lastSubmittedFormId = Lazarus.getFormId(form);        
  518.     }    
  519. }
  520.  
  521. /**
  522. * call autofill if this is a valid html document.
  523. */
  524. Lazarus.autofillEvent = function(evt){
  525.     if (evt.originalTarget instanceof HTMLDocument){
  526.         Lazarus.autofillDoc(evt.originalTarget);
  527.     }
  528. }
  529.  
  530.  
  531. /**
  532. * convert a string of HTML into human readable text
  533. */
  534. Lazarus.htmlToText = function(html){
  535.     
  536.     //replace headings (</h1>) and paragraph ends with 2 line breaks
  537.     var text = html.replace(/\s*<\/((h\d)|p)\s*>\s*/ig," \n\n");
  538.     
  539.     //replace divs blocks with single line breaks
  540.     text = text.replace(/\s*<(\/div)\b[^>]*>\s*/ig,"\n");
  541.     
  542.     //replace list items with line breaks and dots
  543.     text = text.replace(/\s*<(li)\b[^>]*>\s*/ig,"\n * ");
  544.     
  545.     //replace line breaks
  546.     text = text.replace(/<(br)\b[^>]*>/ig,"\n"); 
  547.     
  548.     //strip all other tags
  549.     text = text.replace(/<(\/|\w)[^>]*>/g,' ');
  550.     
  551.     //convert html spaces into normal spaces
  552.     text = text.replace(/ /g, ' ');
  553.     
  554.     //compress whitespace
  555.     text = text.replace(/[ \t\f\v]+/g, ' ');
  556.     
  557.     //never have more than 2 line breaks in a row.
  558.     text = text.replace(/\n\s*?\n(\s*?\n)*/g, "\n\n");
  559.     
  560.     //and finally trim.
  561.     text = text.replace(/^\s+/, '').replace(/\s+$/, '');
  562.     
  563.     return text;
  564. }
  565.  
  566.  
  567. /**
  568. * return TRUE if forms on the given document should be saved 
  569. */
  570. Lazarus.isValidDoc = function(doc){
  571.     return (doc && doc.URL && /^(file|http|https):/.test(doc.URL));
  572. }
  573.  
  574. /**
  575. * autofills forms found in the given document with there respective templates if they exist
  576. */
  577. Lazarus.autofillDoc = function(doc){
  578.  
  579.     if (Lazarus.isValidDoc(doc) && doc.forms && doc.forms.length){
  580.         var rsAutoFillTemplates = Lazarus.db.rs("SELECT id, formid FROM forms WHERE autofill = 1 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY created DESC");
  581.         if (rsAutoFillTemplates.length > 0){
  582.             var templates = {};
  583.             //convert the template id's to a hash table
  584.             //TODO: there should only ever be one autofill template for a given form
  585.             //raise a warning if this is not the case
  586.             for (var i=0; i<rsAutoFillTemplates.length; i++){
  587.                 templates[rsAutoFillTemplates[i]["formid"]] = rsAutoFillTemplates[i]["id"];
  588.             }
  589.             
  590.             for (var i=0; i<doc.forms.length; i++){
  591.                 var form = doc.forms[i];
  592.                 var formId = Lazarus.getFormId(form);
  593.                 //only autofill if the form is empty, and the form hasn't just been submitted.
  594.                 if (formId != Lazarus.lastSubmittedFormId && templates[formId] && Lazarus.isFormEmpty(form)){
  595.                     //if the user has a password and is not yet logged in, then we cannot autofill the form
  596.                     if (Lazarus.canDecrypt()){
  597.                         var encryptedFormInfo = Lazarus.db.getStr("SELECT forminfo FROM forms WHERE id = ?1", templates[formId]);
  598.                         var formInfo = Lazarus.JSON.decode(Lazarus.decrypt(encryptedFormInfo));
  599.                         Lazarus.restoreForm(form, formInfo);
  600.                     }
  601.                     else {
  602.                         //need to explain to user why we cannot autofill the template.
  603.                         Lazarus.showNotificationBox("password-required-autofill");
  604.                         break;
  605.                     }
  606.                 }
  607.             }
  608.         }
  609.     }
  610.     
  611.     //if this is the currently visible tab, then clear the lastSubmittedFormId
  612.     if (content.document && doc === content.document){
  613.         Lazarus.lastSubmittedFormId = null;
  614.     }
  615. }
  616.  
  617. /**
  618. * return TRUE if the given form contains no user entered text.
  619. */
  620. Lazarus.isFormEmpty = function(form){
  621.     for (var i=0; i<form.elements.length; i++){
  622.         var ele = form.elements[i];
  623.         switch (Lazarus.getElementType(ele)){
  624.             case "text":
  625.             case "textarea":
  626.             case "file":
  627.             case "password":
  628.                 var text = Lazarus.getElementValue(ele);
  629.                 if (Lazarus.trim(text)){
  630.                     return false;
  631.                 }
  632.                 
  633.             //ignore other input types
  634.             default:
  635.         }
  636.     }
  637.     return true;
  638. }
  639.  
  640. /**
  641. * initalise any developer specific functionality
  642. */
  643. Lazarus.initDevEnviroment = function(){
  644.     
  645.     if (Lazarus.getExtPref("openErrorConsoleAtStartup", false)){ 
  646.         //open the javascript console
  647.         toJavaScriptConsole();
  648.     }
  649.     
  650.     if (Lazarus.getExtPref("debugMode") >= 4){
  651.         Lazarus.$('lazarus-statusbar-menuitem-test').hidden = false; 
  652.     }
  653. }
  654.  
  655. /**
  656. * handle text within the address bar changing
  657. */
  658. Lazarus.onLocationChange = function(evt){
  659.     //only close the notification if current page was not the result of submitting a form.
  660.     if (!Lazarus.lastSubmittedFormId){
  661.         Lazarus.closeNotificationBox(true);
  662.     }
  663.     Lazarus.refreshIcon();
  664. }
  665.  
  666. /**
  667. * fire the "clear private data" event
  668. */
  669. Lazarus.fireClearPrivateDataIfNoPrompt = function(){
  670.     if (!Lazarus.getPref("privacy.sanitize.promptOnSanitize", true)){
  671.         Lazarus.Event.fire("clear-private-data");
  672.     }
  673. }
  674.  
  675.  
  676. /**
  677. * return the current state of lazarus
  678. */
  679. Lazarus.getState = function(){
  680.     if (!Lazarus.initalized){
  681.         return Lazarus.STATE_UNINITALIZED;
  682.     }
  683.     else if (Lazarus.Crypto.generatingKeys || Lazarus.cleaningDatabase){
  684.         return Lazarus.STATE_GENERATING_KEYS;
  685.     }
  686.     else if (!Lazarus.canEncrypt()){
  687.         return Lazarus.STATE_DISABLED;
  688.     }
  689.     else if (Lazarus.isDisabledByPrivateBrowsing()){
  690.         return Lazarus.STATE_PRIVATE_BROWSING;
  691.     }
  692.     else if (Lazarus.isPageDisabled()){
  693.         return Lazarus.STATE_DISABLED_FOR_DOMAIN;
  694.     }
  695.     else if (!Lazarus.canDecrypt()){
  696.         return Lazarus.STATE_PASSWORD_REQUIRED;
  697.     }
  698.     //all good?
  699.     else {
  700.         return Lazarus.STATE_ENABLED;
  701.     }
  702. }
  703.  
  704. /**
  705. * fix for multiline xul:description elements
  706. */
  707. Lazarus.setDescriptionText = function(ele, text){
  708.     for (var i=ele.childNodes.length-1; i>=0; i--){
  709.         ele.removeChild(ele.childNodes[i]);
  710.     }
  711.     ele.appendChild(ele.ownerDocument.createTextNode(text));
  712. }
  713.  
  714. /**
  715. * updates the statusbar icon
  716. */
  717. Lazarus.refreshIcon = function(){
  718.  
  719.     Lazarus.$("lazarus-statusbarpanel").hidden = !Lazarus.getExtPref("showInStatusbar");
  720.     
  721.     var iconURL = "";
  722.     var tooltipId = "";
  723.      
  724.     switch (Lazarus.getState()){
  725.         case Lazarus.STATE_ENABLED:
  726.             iconURL = "chrome://lazarus/skin/lazarus.png";    
  727.             tooltipId = "lazarus-statusbaricon-tooltip-enabled"         
  728.             break;
  729.         
  730.         case Lazarus.STATE_GENERATING_KEYS: 
  731.             iconURL = 'chrome://lazarus/skin/lazarus-loading.gif';
  732.             tooltipId = 'lazarus-statusbaricon-tooltip-generatingkeys';
  733.             break;
  734.             
  735.         case Lazarus.STATE_PASSWORD_REQUIRED:
  736.             iconURL = "chrome://lazarus/skin/lazarus-login.png"; 
  737.             tooltipId = "lazarus-statusbaricon-tooltip-passwordrequired";     
  738.             break;   
  739.  
  740.         case Lazarus.STATE_DISABLED_FOR_DOMAIN:
  741.             iconURL = "chrome://lazarus/skin/lazarus-disabled.png"; 
  742.             tooltipId = "lazarus-statusbaricon-tooltip-disabledfordomain";     
  743.             break; 
  744.         
  745.         case Lazarus.STATE_PRIVATE_BROWSING:
  746.             iconURL = "chrome://lazarus/skin/lazarus-disabled.png";
  747.             tooltipId = "lazarus-statusbaricon-tooltip-private-browsing";              
  748.             break;
  749.             
  750.         case Lazarus.STATE_UNINITALIZED:
  751.         case Lazarus.STATE_DISABLED:
  752.         default:
  753.             iconURL = "chrome://lazarus/skin/lazarus-disabled.png";
  754.             tooltipId = "lazarus-statusbaricon-tooltip-disabled";
  755.     }
  756.     
  757.     //this appears to screw over firefox, missing images and such, if called during startup?
  758.     //NOTE: firfox must be completely closed for this effect, using "restart" will NOT recreate it.
  759.     //Lazarus.setDescriptionText(Lazarus.$("lazarus-statusbaricon-tooltip-description"), tooltip);
  760.     
  761.     Lazarus.$("lazarus-statusbarpanel-image").setAttribute("src", iconURL);
  762.     Lazarus.$("lazarus-statusbarpanel-image").setAttribute("tooltip", tooltipId);
  763. }
  764.  
  765. /**
  766. * displays the lazarus welcome message
  767. */
  768. Lazarus.showWelcome = function(){
  769.     //dont show the welcome message immediately, need to wait a sec so the 
  770.     //browser is open, and we can center the dialog relative to it.
  771.     //~ setTimeout(function(){
  772.         //~ Lazarus.openOptionsDialog("welcome-pane");
  773.     //~ }, 100);
  774.     
  775.     //hmmm, session recovery is happening after this event, 
  776.     //which is replacing our tab with the recovered sessions tabs
  777.     setTimeout(function(){
  778.         Lazarus.openLazarusWebsite("oninstall.html?ver="+ Lazarus.getVersionStr());
  779.     }, 3000);
  780. }
  781.  
  782. /**
  783. * opens the lazarus onupdate page for this version of lazarus
  784. */
  785. Lazarus.showUpdatePage = function(){
  786.     //hmmm, session recovery is happening after this event, 
  787.     //which is replacing our tab with the recovered sessions tabs
  788.     setTimeout(function(){
  789.         Lazarus.openLazarusWebsite("onupdate.html?ver="+ Lazarus.getVersionStr());
  790.     }, 3000);
  791. }
  792.  
  793.  
  794. /**
  795. * handle when the first browser window is opened.
  796. */
  797. Lazarus.onStartUp = function(){
  798.     
  799. }
  800.  
  801. /**
  802. * onUninstall
  803. */
  804. Lazarus.onUninstall = function(ext){
  805.     
  806.     if (ext.id == Lazarus.guid){
  807.         
  808.         //var msg = '';
  809.         if (Lazarus.getExtPref("uninstall.removeSavedForms", false)){
  810.             //remove database
  811.             //unable to disconnect the database, so the best we can do is empty it. 
  812.             Lazarus.emptyDB();
  813.             
  814.         }
  815.         if (Lazarus.getExtPref("uninstall.removeUserSettings", false)){
  816.             //cleanup prefs 
  817.             Lazarus.killPref("extensions.lazarus");
  818.             Lazarus.Pref.savePrefFile();
  819.         }
  820.     }
  821. }
  822.  
  823. /**
  824. */
  825. Lazarus.onUninstallRequest = function(ext){
  826.     if (ext.id == Lazarus.guid){
  827.         //ask the user if we should remove their preferences/saved forms
  828.         //we need this dialog to appear from the addons dialog (if it exists)
  829.         //~ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
  830.         //~ var win = wm.getMostRecentWindow("Extension:Manager");
  831.         
  832.         //~ //if theres no extension manager window (eg all-in-one-sidebar)
  833.         //~ //use this window to open the dialog
  834.         //~ if (!win){
  835.             //~ win = window;
  836.         //~ }
  837.         //disabling dialog, uninstall process is stuffed up
  838.         //win.openDialog("chrome://lazarus/content/uninstall.xul", "LazarusUninstallOptions", "chrome,dialog,modal,resizable=yes,titlebar=yes");
  839.     }
  840. }
  841.  
  842.  
  843. /**
  844. * return TRUE if the given editor still exists in the browser
  845. */
  846. Lazarus.editorExists = function(info){
  847.     try {
  848.         //KLUDGE:
  849.         //if a page is refreshed then the page remains, even if you then navigate away from it.
  850.         //it appears to still exist, even though the user cannot see it.
  851.         if (Lazarus.isIframe(info.editor) && (!info.editor.contentWindow || !info.editor.contentWindow.document)){
  852.             return false;
  853.         }
  854.         else {
  855.             return (info.editor.ownerDocument.defaultView && (info.url == info.editor.ownerDocument.defaultView.top.location.href));
  856.         }
  857.     }
  858.     catch(e){
  859.         return false;
  860.     }
  861. }
  862.  
  863.  
  864. Lazarus.saveEditorInfo = function(info, saveType){
  865.  
  866.  
  867.     if (Lazarus.shouldSaveEditorInfo(info)){
  868.  
  869.         var textHash = Lazarus.md5(info.text);
  870.         
  871.         if (saveType == Lazarus.FORM_TYPE_AUTOSAVE){
  872.             var record = Lazarus.db.getRow("SELECT id, text_hash FROM textdata WHERE domain_hash = ?1 AND savetype = ?2 LIMIT 1", info.domainHash, Lazarus.FORM_TYPE_AUTOSAVE);
  873.             //if form hasn't changed
  874.             if (record && record.text_hash == textHash){
  875.                 //do nothing...
  876.             }
  877.             //if it has changed, or doesn't exist yet, save the changes
  878.             else {
  879.                 Lazarus.debug("Saving textdata - autosave", info);
  880.                 if (record){
  881.                     Lazarus.db.exe("DELETE FROM textdata WHERE id = ?1", record.id);
  882.                     //and get rid of any fulltext index as well
  883.                     Lazarus.db.exe("DELETE FROM textdata_fulltext WHERE docid = ?1", record.id);
  884.                 }
  885.                 var encText = Lazarus.encrypt(info.text);
  886.                 var encSummary = Lazarus.encrypt(info.summary);
  887.                 
  888.                 var id = Lazarus.db.insert("INSERT INTO textdata (text_encrypted, summary_encrypted, created, domain_hash, url_encrypted, text_hash, text_length, savetype) \
  889.                     VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", 
  890.                     encText, encSummary, info.created, info.domainHash, info.urlEncrypted, textHash, info.text.length, Lazarus.FORM_TYPE_AUTOSAVE);
  891.                 
  892.                 //and update the full text index,
  893.                 if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
  894.                     //we're going to add domain info into this as well
  895.                     var hashedText = Lazarus.hashText(info.text);
  896.                     hashedText += ' '+ Lazarus.hashText(info.domain.replace(/\./g, ' '));
  897.                     Lazarus.db.exe("INSERT INTO textdata_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
  898.                 }
  899.             }
  900.         }
  901.         else {
  902.             
  903.             //if the info object already exists, with exactly the same text, then just update the timestamp
  904.             var id = Lazarus.db.getInt("SELECT id FROM textdata WHERE domain_hash = ?1 AND text_hash = ?2 LIMIT 1", info.domainHash, textHash);
  905.             if (id){
  906.                 Lazarus.debug("Updating textdata - perm", info);
  907.                 Lazarus.db.exe("UPDATE textdata SET created = ?1, savetype = ?2 WHERE id = ?3", info.created, Lazarus.FORM_TYPE_NORMAL, id);
  908.             }
  909.             //otherwise, insert a new info object
  910.             else {
  911.                 Lazarus.debug("Saving textdata - perm", info);
  912.                 var encText = Lazarus.encrypt(info.text);
  913.                 var encSummary = Lazarus.encrypt(info.summary);
  914.                 
  915.                 var id = Lazarus.db.insert("INSERT INTO textdata (text_encrypted, summary_encrypted, created, domain_hash, url_encrypted, text_hash, text_length, savetype) \
  916.                     VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", 
  917.                     encText, encSummary, info.created, info.domainHash, info.urlEncrypted, textHash, info.text.length, Lazarus.FORM_TYPE_NORMAL);
  918.                 
  919.                 if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
  920.                     var hashedText = Lazarus.hashText(info.text);
  921.                     hashedText += ' '+ Lazarus.hashText(info.domain.replace(/\./g, ' '));
  922.                     Lazarus.db.exe("INSERT INTO textdata_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
  923.                 }
  924.             }
  925.         }
  926.     }
  927. }
  928.  
  929.  
  930. /**
  931. * genereate a random seed for use in the hashing function
  932. */
  933. Lazarus.generateRandomHashSeed = function(){
  934.     var rnd = Math.random().toString() +':'+ Lazarus.timestamp(true).toString();
  935.     return Lazarus.FNV1a(rnd);
  936. }
  937.  
  938.  
  939. /**
  940. * hashes individual words in a bunch of text
  941. */
  942. Lazarus.hashText = function(text){
  943.  
  944.     //we'll need to grab the seed from the database.
  945.     var seed = Lazarus.db.getStr("SELECT value FROM settings WHERE name = 'hash-seed'");
  946.     
  947.     if (!seed){
  948.         seed = Lazarus.generateRandomHashSeed();
  949.         Lazarus.db.exe("INSERT INTO settings (name, value) VALUES ('hash-seed', ?1)", seed);
  950.         //and delete the full text index, because none of the others will work any more
  951.         Lazarus.db.exe("DELETE FROM textdata_fulltext");      
  952.     }
  953.     
  954.     //var p = new Profiler("hashedText: "+ text.length);
  955.     var map = {};
  956.     //p.mark("setup");
  957.     
  958.     //remove all non-text characters (strip HTML?)
  959.     text = Lazarus.trim(text.toLowerCase().replace(/[^\s\w\-_]+/g, ' '));
  960.     //p.mark("remove non-text: "+ text.length);
  961.     
  962.     var words = text.split(/\s+/g);
  963.     //p.mark("split into words : "+ words.length);
  964.         //we should also remove useless words (like "the", "and", "as" etc...)
  965.     var len = words.length;
  966.     var hashedWords = [];
  967.     for (var i=0; i<len; i++){
  968.         var word = words[i];        
  969.         if (!map[word]){
  970.             map[word] = Lazarus.FNV1a(word, seed);
  971.         }
  972.         hashedWords.push(map[word]);
  973.     }
  974.     //p.mark("hash words");
  975.     
  976.     //hashed text comes out at 70,000 characters for a 56,000 character start.
  977.     //acceptable.
  978.     var hashedText = hashedWords.join(" ");
  979.     //debug(p.stop("join text: "+ hashedText.length));
  980.     return hashedText; 
  981. }
  982.  
  983.  
  984. /**
  985. * hash text within a MATCH query whilst keeping SQLite MATCH keywords/characters
  986. */
  987. Lazarus.hashQuery = function(query){
  988.     //phrase searches ('"broccoli cheese"')
  989.     //Excluding terms ('onions -celery')
  990.     //OR queries ('onions OR cheese')
  991.     //Prefix search ('ch*') cannot be used due to hashing of the text strings.
  992.     return query.replace(/\w+/g, function(m){
  993.         if (m.toLowerCase() == "or"){
  994.             return "OR";
  995.         }
  996.         else {
  997.             return Lazarus.hashText(m);
  998.         }
  999.     });
  1000. }
  1001.  
  1002. Lazarus.saveAutoSaveText = function(){
  1003.     Lazarus.db.exe("UPDATE textdata SET savetype = ?1 WHERE savetype = ?2", Lazarus.FORM_TYPE_NORMAL, Lazarus.FORM_TYPE_AUTOSAVE);
  1004. }
  1005.  
  1006. Lazarus.autoSaveEditors = function(){
  1007.     
  1008.     //NOTE: traversing backwards through the list so we can remove items without it affecting 
  1009.     //the rest of the list
  1010.     var removed = [];
  1011.     
  1012.     for (var i=Lazarus.editorInfos.length-1; i>=0; i--){
  1013.         var info = Lazarus.editorInfos[i];
  1014.         //the page may have been submitted, or closed without submitting
  1015.         //if the editor is suddenly empty, we should assume it has been submitted (possibly via AJAX)
  1016.         if (!Lazarus.editorExists(info) || Lazarus.getEditorInfo(info.editor).isEmpty){
  1017.             removed.push(info);
  1018.             Lazarus.editorInfos.splice(i, 1);
  1019.         }
  1020.     }
  1021.     
  1022.     //and save any that have been removed
  1023.     for (var i=0; i<removed.length; i++){
  1024.         Lazarus.saveEditorInfo(removed[i]);
  1025.     }
  1026.     
  1027.     //we also want to update the current textbox (if any)
  1028.     
  1029.     //all others should be saved as "temporary saves"
  1030.     for (var i=0; i<Lazarus.editorInfos.length; i++){
  1031.         if (Lazarus.editorInfos[i].editor === Lazarus.currentEditor){
  1032.             Lazarus.saveEditorInfo(Lazarus.editorInfos[i], Lazarus.FORM_TYPE_AUTOSAVE);
  1033.             break;
  1034.         }
  1035.     }
  1036.     
  1037.     if (Lazarus.editorInfos.length == 0){
  1038.         Lazarus.stopEditorAutoSaveTimer();
  1039.     }
  1040. }
  1041.  
  1042.  
  1043. /**
  1044. * return the previous editosInfo 
  1045. */
  1046. Lazarus.getPreviousEditorInfo = function(editor){
  1047.     for (var i=0; i<Lazarus.editorInfos.length; i++){
  1048.         if (Lazarus.editorInfos[i].editor === editor){
  1049.             return Lazarus.editorInfos[i];
  1050.         }
  1051.     }
  1052.     return null;
  1053. }
  1054.  
  1055.  
  1056. /**
  1057. * remove an editor info object from our list
  1058. */
  1059. Lazarus.removeEditorInfo = function(info){
  1060.     for (var i=0; i<Lazarus.editorInfos.length; i++){
  1061.         if (Lazarus.editorInfos[i].editor === info.editor){
  1062.             Lazarus.editorInfos.splice(i, 1);
  1063.             return true;
  1064.         }
  1065.     }
  1066.     return false;
  1067. }
  1068.  
  1069.  
  1070. /**
  1071. * update editor info
  1072. */
  1073. Lazarus.updateEditorInfo = function(info){
  1074.     for (var i=0; i<Lazarus.editorInfos.length; i++){
  1075.         if (Lazarus.editorInfos[i].editor === info.editor){
  1076.             Lazarus.editorInfos[i] = info;
  1077.             return true;
  1078.         }
  1079.     }
  1080.     return false;
  1081. }
  1082.  
  1083.  
  1084.  
  1085. /**
  1086. * save forms if people are typing into them
  1087. */
  1088. Lazarus.onKeyUp = function(evt){
  1089.     //dont save form for every keypress (CPU hog)
  1090.     //we'll restart a timer whenever a key is pressed, 
  1091.     //and save when the timer fires.
  1092.     var form = Lazarus.findFormFromElement(evt.target);
  1093.     
  1094.     if (Lazarus.isPageDisabled()){
  1095.         return;
  1096.     }
  1097.     
  1098.     //if the event happened within a form, set a timer to auto save the form
  1099.     if (form && form.ownerDocument instanceof HTMLDocument){
  1100.         Lazarus.restartAutoSaveTimer(form);
  1101.     }
  1102.     
  1103.     //we'll do the same for textarea's and contentEditable iframes
  1104.     var editor = Lazarus.findEditorFromElement(evt.target);
  1105.     
  1106.     if (editor){
  1107.     
  1108.         Lazarus.currentEditor = editor;
  1109.         
  1110.         var info = Lazarus.getEditorInfo(editor);
  1111.         var prevInfo = Lazarus.getPreviousEditorInfo(editor);
  1112.         
  1113.         if (prevInfo){
  1114.             //if the textbox is suddenly empty, the form may have been submitted or reset (or all contents removed (ctrl+a + del))
  1115.             if (info.isEmpty){
  1116.                 Lazarus.saveEditorInfo(prevInfo);
  1117.                 //then remove the editorInfo from the list
  1118.                 Lazarus.removeEditorInfo(prevInfo);
  1119.             }
  1120.             //otherwise update the previous editorInfo, and wait for the timer to save it.
  1121.             else {
  1122.                 Lazarus.updateEditorInfo(info);
  1123.             }
  1124.         }
  1125.         //this is the first time the user has typed into this textbox
  1126.         //save it, if it's got some text in it
  1127.         else if (!info.isEmpty){
  1128.             Lazarus.editorInfos.push(info);
  1129.             Lazarus.restartEditorAutoSaveTimer();
  1130.         }
  1131.         else {
  1132.             //editor is empty, dont keep it
  1133.         }
  1134.         
  1135.         Lazarus.restartEditorAutoSaveTimer();
  1136.     }
  1137. }
  1138.  
  1139.  
  1140. /**
  1141. * return an editor info object
  1142. */
  1143. Lazarus.getEditorInfo = function(editor){
  1144.     var info = {}
  1145.     info.editor = editor;
  1146.     info.text = Lazarus.extractText(editor);
  1147.     info.isEmpty = Lazarus.isEditorEmpty(editor);
  1148.     info.summary = Lazarus.generateSummary(info.text);
  1149.     info.created = Lazarus.timestamp();
  1150.     info.domain = Lazarus.getDomainFromElement(editor);
  1151.     info.domainHash = Lazarus.md5(info.domain);
  1152.     info.basedomain = Lazarus.getBaseDomain(info.domain);
  1153.     info.url = editor.ownerDocument.defaultView.top.location.href;
  1154.     info.urlEncrypted = Lazarus.encrypt(info.url);
  1155.     return info;
  1156. }
  1157.  
  1158.  
  1159. /**
  1160. * return TRUE if the editor doesn't contain any text 
  1161. */
  1162. Lazarus.isEditorEmpty = function(editor){
  1163.     var text = Lazarus.extractText(editor);
  1164.     var type = editor.nodeName.toLowerCase();
  1165.     if (type == "textarea"){
  1166.         return text.match(/^\s*$/) ? true : false;
  1167.     }
  1168.     else if (type == "iframe"){
  1169.         return text.replace(/(<(\/|\w)[^>]*>)|( )/g, '').match(/^\s*$/) ? true : false;
  1170.     }
  1171.     else {
  1172.         throw Error("Unknown editor type: "+ type);
  1173.     }
  1174. }
  1175.  
  1176. /**
  1177. * return the text/html from an editable iframe or textarea
  1178. */
  1179. Lazarus.extractText = function(ele){
  1180.     var text = '';
  1181.     if (Lazarus.isTextarea(ele)){
  1182.         text = (typeof ele.value == "string") ? ele.value : '';
  1183.     }
  1184.     else if (Lazarus.isIframe(ele)){
  1185.         text = (typeof ele.contentWindow.document.body.innerHTML) ? ele.contentWindow.document.body.innerHTML : '';
  1186.     }
  1187.     return Lazarus.trim(text);
  1188. }
  1189.  
  1190.  
  1191. /**
  1192. * generate a safe summary for display within a XUL:menuitem or XUL:tooltip
  1193. */
  1194. Lazarus.generateSummary = function(text){
  1195.     //strip html
  1196.     text = Lazarus.htmlToText(text);
  1197.     return (text.length > 255) ? (text.substr(0, 252) +"...") : text;
  1198. }
  1199.  
  1200. Lazarus.restartEditorAutoSaveTimer = function(){
  1201.     Lazarus.stopEditorAutoSaveTimer();
  1202.     Lazarus.editorAutoSaveFormTimer = setInterval(Lazarus.autoSaveEditors, Lazarus.getExtPref("autoSaveInterval", 2000));
  1203. }
  1204. Lazarus.stopEditorAutoSaveTimer = function(){
  1205.     if (Lazarus.editorAutoSaveFormTimer){
  1206.         clearInterval(Lazarus.editorAutoSaveFormTimer);
  1207.     }
  1208. }
  1209.  
  1210.  
  1211. /**
  1212. * start the autosave timer 
  1213. */
  1214. Lazarus.restartAutoSaveTimer = function(form){
  1215.     Lazarus.stopAutoSaveTimer();
  1216.     //we'll save the form here, so we can retrieve it when the timer fires
  1217.     Lazarus.currAutoSaveForm = form;
  1218.     Lazarus.autoSaveFormTimer = setTimeout(Lazarus.autoSaveForm, Lazarus.getExtPref("autoSaveInterval", 2000));
  1219. }
  1220.  
  1221. /**
  1222. * stops the autosave timer.
  1223. */
  1224. Lazarus.stopAutoSaveTimer = function(){
  1225.     if (Lazarus.autoSaveFormTimer){
  1226.         clearTimeout(Lazarus.autoSaveFormTimer);
  1227.     }
  1228. }
  1229.  
  1230. /**
  1231. * run cleanup for this window
  1232. */
  1233. Lazarus.cleanup = function(){
  1234.     gBrowser.removeEventListener("keyup", Lazarus.onKeyUp, false);
  1235.     gBrowser.removeEventListener("submit", Lazarus.onFormSubmit, false);
  1236.     gBrowser.removeEventListener("reset", Lazarus.onFormReset, false);
  1237.     gBrowser.removeEventListener("change", Lazarus.onFormChange, false);
  1238.     Lazarus.$("Tools:Sanitize").removeEventListener("command", Lazarus.fireCLearPrivateDataIfNoDialog, false);
  1239.     Lazarus.stopCleanupTimer();
  1240. }
  1241.  
  1242. /**
  1243. * handle last browser window shutting down 
  1244. */
  1245. Lazarus.onShutdown = function(){
  1246.     
  1247. }
  1248.  
  1249. /**
  1250. */
  1251. Lazarus.onContextMenuHide = function(){
  1252.     Lazarus.isContextMenuShowing = false;
  1253. }
  1254.  
  1255.  
  1256. /**
  1257. * show or hide the menu item depending on what item caused the context menu to appear.
  1258. */
  1259. Lazarus.onContextMenuShowing = function(evt){
  1260.  
  1261.     //we need to set a flag to prevent new autosaves whilst the context menu is shown
  1262.     Lazarus.isContextMenuShowing = true;
  1263.     
  1264.     //this event is fired whenever a submenu is shown, as well as when the main context menu it shown 
  1265.     //bugfix: only re-calculate the popup menu when it's first opened.
  1266.     //trying to alter the initial menu when a submenu is opened can cause the browser to hang.
  1267.  
  1268.     //hmmm this is causing a noticable hang when the form contains a lot (30k) of info
  1269.     //as a workaround, we will not calculate any submenu until the submenu is opened.
  1270.     var evtTargetId = evt.target.id;
  1271.     
  1272.     //quick check 
  1273.     if (evtTargetId != "contentAreaContextMenu" && evtTargetId != "lazarus-restoreform-submenu-menupopup" && evtTargetId != "lazarus-restoretext-submenu-menupopup"){
  1274.         return;
  1275.     }
  1276.     
  1277.     
  1278.     //did the user click on a form?
  1279.     var form = Lazarus.findFormFromElement(gContextMenu.target);
  1280.     var editor = Lazarus.findEditorFromElement(gContextMenu.target);
  1281.     
  1282.     if (evtTargetId == "contentAreaContextMenu"){
  1283.             
  1284.         //assume we should not show any menuitems
  1285.         var showMainMenu = false;
  1286.         var showSubMenu = false;
  1287.         var showLogin = false;
  1288.         var showSaveForm = false;
  1289.         var showPageDisabled = false;
  1290.         var showPrivateBrowsing = false;
  1291.         
  1292.         var showRestoreText = false;
  1293.         var showRestoreTextDisabled = false;
  1294.         
  1295.     
  1296.         if (!form && !editor){
  1297.             //dont show anything
  1298.         }
  1299.         else if (Lazarus.isDisabledByPrivateBrowsing()){
  1300.             showPrivateBrowsing = true;
  1301.         }
  1302.         else if (Lazarus.isPageDisabled(form.ownerDocument.URL)){
  1303.             showPageDisabled = true;
  1304.         }
  1305.         else if (!Lazarus.canDecrypt()){  
  1306.             showLogin = true;
  1307.         }
  1308.         else {
  1309.             if (form){ 
  1310.                 
  1311.                 showSaveForm = true;   
  1312.                 var savedForms = Lazarus.getFormInfo(form, "id, formid, created, savetype, forminfohash, formtext, formname");
  1313.                 
  1314.                 //just show the one menu item, but dont show it if the form is identical to this one.
  1315.                 if (savedForms.length == 1){
  1316.                     showMainMenu = true;
  1317.                     var info = Lazarus.formInfo(form);
  1318.                     
  1319.                     var infoHash = Lazarus.generateHash(info.fields);
  1320.                     
  1321.                     if (infoHash == savedForms[0]["forminfohash"]){
  1322.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('src', "chrome://lazarus/skin/lazarus-disable.png");
  1323.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('disabled', "true");
  1324.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('tooltiptext', Lazarus.getString("form.is.equal"));
  1325.                     }
  1326.                     else {
  1327.                         var savedForm = savedForms[0];
  1328.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('src', "chrome://lazarus/skin/lazarus.png");
  1329.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('lazarus-forms-id', savedForm["id"]);
  1330.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('tooltiptext', Lazarus.generateSavedFormTooltip(savedForm));
  1331.                         Lazarus.$('lazarus-restoreform-contextmenuitem').setAttribute('disabled', "");    
  1332.                     }        
  1333.                 }
  1334.                 //show submenu 
  1335.                 else if (savedForms.length > 1){
  1336.                     showSubMenu = true;
  1337.                 }
  1338.             }
  1339.             
  1340.             if (editor){
  1341.                 showRestoreTextDisabled = true;
  1342.                 //do we have any text saved for this domain/basedomain?
  1343.                 var domain = Lazarus.getDomainFromElement(editor);
  1344.                 if (domain){
  1345.                     var domainHash = Lazarus.md5(domain);
  1346.                     if (Lazarus.db.getInt("SELECT count(id) FROM textdata WHERE domain_hash = ?1 LIMIT 1", domainHash)){
  1347.                         showRestoreText = true;
  1348.                         showRestoreTextDisabled = false;
  1349.                     }
  1350.                 }
  1351.             }
  1352.         }
  1353.         
  1354.         //only show our menu item if over a form.
  1355.         Lazarus.$('lazarus-restoretextdisabled-contextmenuitem').hidden = !showRestoreTextDisabled;
  1356.         Lazarus.$('lazarus-restoretext-submenu').hidden = !showRestoreText;
  1357.         Lazarus.$('lazarus-restoreform-contextmenuitem').hidden = !showMainMenu;
  1358.         Lazarus.$('lazarus-restoreform-submenu').hidden = !showSubMenu;
  1359.         Lazarus.$('lazarus-enterpassword-contextmenuitem').hidden = !showLogin;
  1360.         Lazarus.$('lazarus-domaindisabled-contextmenuitem').hidden = !showPageDisabled;
  1361.         Lazarus.$('lazarus-privatebrowsing-contextmenuitem').hidden = !showPrivateBrowsing;
  1362.         Lazarus.$('lazarus-saveform-contextmenuitem').hidden = !(showSaveForm && Lazarus.getExtPref("includeExperimental", false));
  1363.     }
  1364.     else if (evtTargetId == "lazarus-restoreform-submenu-menupopup" && form){
  1365.         Lazarus.buildSubMenu(form);
  1366.     }
  1367.     else if (evtTargetId == "lazarus-restoretext-submenu-menupopup" && editor){
  1368.         Lazarus.buildRestoreTextSubMenu(editor);
  1369.     }
  1370. }
  1371.  
  1372.  
  1373.  
  1374. Lazarus.getDomainFromElement = function(ele){
  1375.     try {
  1376.         return ele.ownerDocument.defaultView.top.location.host;
  1377.     }
  1378.     catch(e){
  1379.         return null;
  1380.     }
  1381.  
  1382. /**
  1383. * builds the list of items that can be restored for this editor element
  1384. */
  1385. Lazarus.buildRestoreTextSubMenu = function(editor){
  1386.     //
  1387.     var menu = Lazarus.$("lazarus-restoretext-submenu-menupopup");
  1388.     //remove all the current submenu items
  1389.     while(menu.lastChild){
  1390.         menu.removeChild(menu.lastChild);
  1391.     }
  1392.     
  1393.     var domainHash = Lazarus.md5(Lazarus.getDomainFromElement(editor));
  1394.     
  1395.     //and build the new ones
  1396.     var items = Lazarus.db.rs("SELECT id, text_hash, summary_encrypted, created FROM textdata WHERE domain_hash = ?1 ORDER BY created DESC LIMIT ?2", domainHash, Lazarus.getPref('extensions.lazarus.maxTextItemsInSubmenu', 20));
  1397.     
  1398.     var text = Lazarus.extractText(editor);
  1399.     var textHash = Lazarus.md5(text);
  1400.     
  1401.     for (var i=0; i<items.length; i++){
  1402.         var item = items[i];
  1403.         var menuitem = document.createElement("menuitem");
  1404.         
  1405.         if (Lazarus.getExtPref("showFormTextInSubMenu") && Lazarus.canDecrypt()){
  1406.             var summary = Lazarus.decrypt(item["summary_encrypted"]);
  1407.             menuitem.setAttribute("label", summary);
  1408.             menuitem.setAttribute("tooltiptext", summary);
  1409.         }
  1410.         else {
  1411.             var time = Lazarus.timestamp();
  1412.             menuitem.setAttribute("label", Lazarus.getTimeString(time - item["created"]));
  1413.             //show the tooltip if possible
  1414.             if (Lazarus.canDecrypt()){
  1415.                 menuitem.setAttribute("tooltiptext", Lazarus.decrypt(item["summary_encrypted"]));
  1416.             }
  1417.         }
  1418.         
  1419.         menuitem.setAttribute("lazarus-restoretext-id", item["id"]);
  1420.         menuitem.setAttribute("oncommand", "Lazarus.onRestoreTextMenuItem(this)");
  1421.                     
  1422.         //disable the menu item if the form is identical
  1423.         if (item["text_hash"] == textHash){
  1424.             menuitem.setAttribute('tooltiptext', Lazarus.getString("text.is.equal"));
  1425.             menuitem.setAttribute('disabled', "true");
  1426.         }
  1427.         menu.appendChild(menuitem);
  1428.     }
  1429. }
  1430.  
  1431. /**
  1432. */
  1433. Lazarus.getFormRestoredNotificationText = function(charsRestored, msgId){
  1434.     //we're trying out a bunch of text at the moment
  1435.     var numChars = Lazarus.formatNumber(charsRestored);
  1436.     var numForms = Lazarus.formatNumber(Lazarus.db.getInt("SELECT COUNT(*) FROM forms"));
  1437.     var numText = Lazarus.formatNumber(Lazarus.db.getInt("SELECT COUNT(*) FROM textdata"));
  1438.     
  1439.     var msgs = [
  1440.         'Lazarus has just saved you from having to retype '+ numChars +' characters. If you feel this has helped you, then please consider donating to this project so we can make Lazarus even better.',
  1441.         'Lazarus restored '+ numChars +' characters.\nDatabase contains '+ numForms +' Forms and '+ numText +' Text-blocks.',
  1442.         'Lazarus restored '+ numChars +' characters.\nDatabase contains '+ numForms +' Saved Forms and '+ numText +' Textareas.',
  1443.         'Lazarus resurrected '+ numChars +' characters.\nLazarus is securely storing '+ numForms +' Forms and '+ numText +' Text-blocks'
  1444.     ];
  1445.     
  1446.     return msgs[msgId];
  1447. }
  1448.  
  1449.  
  1450. /**
  1451. */
  1452. Lazarus.onRestoreTextMenuItem = function(menuitem){
  1453.     if (!Lazarus.canDecrypt()){
  1454.         Lazarus.debug("Unable to restore form, password required");
  1455.         Lazarus.showNotificationBox("password-required"); 
  1456.         return;
  1457.     }
  1458.     
  1459.     var editor = Lazarus.findEditorFromElement(gContextMenu.target);
  1460.     
  1461.     if (editor){
  1462.         var id = parseInt(menuitem.getAttribute("lazarus-restoretext-id"));
  1463.         
  1464.         Lazarus.debug("Attempting to restore text: "+ id);
  1465.         
  1466.         var enc_text = Lazarus.db.getStr("SELECT text_encrypted FROM textdata WHERE id = ?1", id);
  1467.         if (enc_text){
  1468.             var text = Lazarus.decrypt(enc_text);
  1469.             if (text){
  1470.                 Lazarus.setElementValue(editor, text);   
  1471.                 Lazarus.incPref("extensions.lazarus.restoreFormCount");
  1472.                 if ((text.length > Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION) && Lazarus.getPref("extensions.lazarus.showDonateNotification") && Lazarus.getPref("extensions.lazarus.restoreFormCount", 0) > 3){
  1473.                     var msgId = Math.floor(Math.random() *  4);
  1474.                     Lazarus.showNotificationBox("form-restored", Lazarus.getFormRestoredNotificationText(text.length, msgId), "msgid-"+ msgId);                        
  1475.                 }
  1476.             }
  1477.             else {
  1478.                 alert(Lazarus.getString("error.form.db.corrupt"));
  1479.             }
  1480.         }
  1481.         else {
  1482.             Lazarus.error("Unable to find textdata: "+ id);
  1483.             alert(Lazarus.getString("error.form.not.found"));
  1484.         }
  1485.     }
  1486.     else {
  1487.         //should never get here
  1488.         alert(Lazarus.getString("error.form.object.not.found"));
  1489.     }
  1490. }
  1491.  
  1492.  
  1493. /**
  1494. * generates an md5 hash of a javascript object
  1495. */
  1496. Lazarus.generateHash = function(obj){
  1497.     return Lazarus.md5(Lazarus.JSON.encode(obj));
  1498. }
  1499.  
  1500. /**
  1501. * return the current unix timestamp
  1502. */
  1503. Lazarus.timestamp = function(asFloat){
  1504.     var s = new Date().getTime() / 1000;
  1505.     return asFloat ? s : Math.floor(s);
  1506. }
  1507.  
  1508. /**
  1509. * return text usable by a menuitem (XUL Label)
  1510. */
  1511. Lazarus.getMenuItemText = function(text){
  1512.     
  1513.         //strip any html found in the text
  1514.         text = Lazarus.trim(Lazarus.cleanText(text));
  1515.         
  1516.     //labels only handle a single line of text
  1517.     text = text.split(/\n/, 2)[0];
  1518.     //and we dont want it to be too long
  1519.     if (text.length > 48){
  1520.         text = text.substr(0, 48) +"...";
  1521.     }
  1522.         return Lazarus.trim(text);
  1523. }
  1524.  
  1525.  
  1526. /**
  1527. * ask the user for their Lazarus password
  1528. */
  1529. Lazarus.showEnterPasswordDialog = function(){
  1530.  
  1531.     var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  1532.     
  1533.     //keep asking until they hit cancel
  1534.     while(true){
  1535.         var password = {value: ""};
  1536.         var check = {value: false};
  1537.         //if a user has a master password set, then allow the lazarus password to be saved in the SSD
  1538.         var checkText = Lazarus.isMasterPasswordSet() ? Lazarus.getString('password.dialog.checkbox.label') : null;
  1539.         //
  1540.         if (prompts.promptPassword(null, Lazarus.getString("password.dialog.title"), Lazarus.getString("password.dialog.label"), password, checkText, check)){
  1541.             if (Lazarus.loadPrivateKey(password.value)){
  1542.                 if (check.value){
  1543.                     Lazarus.savePassword(password.value);
  1544.                 }
  1545.                 return true;
  1546.             }
  1547.         }
  1548.         else {
  1549.             break;
  1550.         }
  1551.     }
  1552.     
  1553.     return false;
  1554. }
  1555.  
  1556.  
  1557. Lazarus.savePassword = function(password){
  1558.     //remove the existing password first.
  1559.     Lazarus.removePassword();
  1560.     var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Components.interfaces.nsILoginInfo, "init");
  1561.     var loginInfo = new nsLoginInfo(Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM, Lazarus.LOGIN_USERNAME, password, '', '');
  1562.     var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
  1563.     nsLoginManager.addLogin(loginInfo);
  1564. }
  1565.  
  1566.  
  1567. Lazarus.removePassword = function(){
  1568.     
  1569.     // Get Login Manager 
  1570.     var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
  1571.     // Find users for this extension 
  1572.     var logins = nsLoginManager.findLogins({}, Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM);
  1573.     for (var i = 0; i < logins.length; i++) {
  1574.         if (logins[i].username == Lazarus.LOGIN_USERNAME){
  1575.             nsLoginManager.removeLogin(logins[i]);
  1576.         }
  1577.     }
  1578. }
  1579.  
  1580. /**
  1581. * load the password from the loginManager
  1582. */
  1583. Lazarus.loadPassword = function(){
  1584.     
  1585.     if (Lazarus.isMasterPasswordSet() && !Lazarus.isMasterPasswordRequired()){
  1586.         // Get Login Manager 
  1587.         var nsLoginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
  1588.  
  1589.         // Find users for the given parameters
  1590.         var logins = nsLoginManager.findLogins({}, Lazarus.LOGIN_HOSTNAME, null, Lazarus.LOGIN_REALM);
  1591.         
  1592.         // Find user from returned array of nsILoginInfo objects
  1593.         for (var i = 0; i < logins.length; i++) {
  1594.             if (logins[i].username == Lazarus.LOGIN_USERNAME){
  1595.                 return logins[i].password;
  1596.             }
  1597.         }
  1598.     }
  1599.     //we default to an empty password
  1600.     return '';
  1601. }
  1602.  
  1603. Lazarus.logout = function(){
  1604.     Lazarus.unloadPrivateKey();
  1605.     Lazarus.refreshIcon();
  1606. }
  1607.  
  1608. /**
  1609. * generate a label for saved form menuitem
  1610. */
  1611. Lazarus.generateSavedFormLabel = function(savedForm, time){
  1612.     var label = '';
  1613.     
  1614.     //template should ALWAYS use their template name as a label
  1615.     if (savedForm["savetype"] == Lazarus.FORM_TYPE_TEMPLATE){
  1616.         label = savedForm["formname"];
  1617.     }
  1618.     //use summary 
  1619.     else if (Lazarus.getExtPref("showFormTextInSubMenu") && Lazarus.canDecrypt()){
  1620.         label = Lazarus.getMenuItemText(Lazarus.decrypt(savedForm["formtext"])) || ("["+ Lazarus.getString("untitled") +"]");
  1621.     }
  1622.         //use timestamps if we are unable to decrypt the forms
  1623.     else {
  1624.         time = time || Lazarus.timestamp();
  1625.         label = Lazarus.getTimeString(time - savedForm["created"]);
  1626.     }
  1627.     
  1628.     //mark autosaved forms so users can easily tell the difference.
  1629.     switch(savedForm["savetype"]){
  1630.         case Lazarus.FORM_TYPE_AUTOSAVE:
  1631.         case Lazarus.FORM_TYPE_STALE_AUTOSAVE:
  1632.             label += " "+ Lazarus.getString("menuitem.label.append.autosave");
  1633.             break;
  1634.         
  1635.         case Lazarus.FORM_TYPE_TEMPLATE:
  1636.             label += " "+ Lazarus.getString("menuitem.label.append.template");
  1637.             break;
  1638.             
  1639.         default:
  1640.             label += " "+ Lazarus.getString("menuitem.label.append.normal");
  1641.             break;
  1642.     }
  1643.     
  1644.     return label;
  1645. }
  1646.  
  1647. /**
  1648. * generate a tooltip for saved form menuitem
  1649. */
  1650. Lazarus.generateSavedFormTooltip = function(savedForm, /**/time){
  1651.     
  1652.     time = time || Lazarus.timestamp();
  1653.     
  1654.     var tooltip = '';
  1655.     
  1656.     //show time sting as tooltip
  1657.     if (Lazarus.getExtPref("showFormTextInSubMenu")){
  1658.         
  1659.         tooltip = Lazarus.getTimeString(time - savedForm["created"]);
  1660.     }
  1661.     //show date and time of save
  1662.     else {
  1663.         var date = new Date(savedForm["created"] * 1000);
  1664.         tooltip = Lazarus.formatDate(date);
  1665.     }
  1666.     
  1667.     //mark autosaved forms so users can easily tell the difference.
  1668.     switch(savedForm["savetype"]){
  1669.         case Lazarus.FORM_TYPE_AUTOSAVE:
  1670.         case Lazarus.FORM_TYPE_STALE_AUTOSAVE:
  1671.             tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.autosave");
  1672.             break;
  1673.         
  1674.         case Lazarus.FORM_TYPE_TEMPLATE:
  1675.             tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.template");
  1676.             break;
  1677.             
  1678.         default:
  1679.             tooltip += " "+ Lazarus.getString("menuitem.tooltip.append.normal");
  1680.             break;
  1681.             //do nothing
  1682.     }
  1683.     
  1684.     return tooltip;
  1685. }
  1686.  
  1687. /**
  1688. * builds the list of available restore point into the submenu
  1689. */
  1690. Lazarus.buildSubMenu = function(form){
  1691.  
  1692.     
  1693.     var menu = Lazarus.$("lazarus-restoreform-submenu-menupopup");
  1694.     //remove all the current submenu items
  1695.     for (var i=menu.childNodes.length-1; i>=0; i--){
  1696.         var node = menu.childNodes[i];
  1697.         if (node.getAttribute("lazarus-dynamic-submenu")){
  1698.             menu.removeChild(node);
  1699.         }
  1700.     }
  1701.     
  1702.     var time = Lazarus.timestamp();
  1703.     var separator = Lazarus.$("lazarus-submenu-separator");
  1704.     //now build the new ones
  1705.     var lastSaveType = -1;
  1706.     var savedForms = Lazarus.getFormInfo(form, "id, formid, created, savetype, forminfohash, formtext, formname, forminfo");
  1707.     var info = Lazarus.formInfo(form);
  1708.     var infoHash = Lazarus.generateHash(info.fields);
  1709.     
  1710.     for (var i=0; i<savedForms.length; i++){
  1711.         var savedForm = savedForms[i];
  1712.         var menuitem = document.createElement("menuitem");
  1713.         
  1714.         //generate text for the label
  1715.         //menuitems can only handle a single line of text, and we don't want it to be too long
  1716.         var label = Lazarus.generateSavedFormLabel(savedForm, time);
  1717.         var tooltip = Lazarus.generateSavedFormTooltip(savedForm, time);
  1718.         
  1719.         menuitem.setAttribute("label", label);
  1720.         //menuitem.setAttribute('tooltiptext', tooltip);
  1721.         menuitem.setAttribute('oncommand', "Lazarus.onRestoreFormMenuItem(this)");
  1722.         menuitem.setAttribute('lazarus-forms-id', savedForm["id"]);
  1723.         menuitem.setAttribute('lazarus-dynamic-submenu', "true");
  1724.                     
  1725.         //disable the menu item if the form is identical
  1726.         if (savedForm["forminfohash"] == infoHash){
  1727.             menuitem.setAttribute('tooltiptext', Lazarus.getString("form.is.equal"));
  1728.             menuitem.setAttribute('disabled', "true");
  1729.         }
  1730.         else {
  1731.             menuitem.setAttribute('tooltiptext', tooltip);
  1732.             menuitem.setAttribute('disabled', "");
  1733.         }
  1734.         
  1735.         //add separators between templates, normal saves, and autosaves
  1736.         if (lastSaveType != -1 && lastSaveType !== savedForm["savetype"] && savedForm["savetype"] != Lazarus.FORM_TYPE_STALE_AUTOSAVE){
  1737.             var newSeparator = document.createElement("menuseparator");
  1738.             newSeparator.setAttribute('lazarus-dynamic-submenu', "true");
  1739.             menu.insertBefore(newSeparator, separator);
  1740.         }
  1741.         lastSaveType = savedForm["savetype"];
  1742.         menu.insertBefore(menuitem, separator);
  1743.     }
  1744. }
  1745.  
  1746.  
  1747. /**
  1748. * return the period (in seconds) that forms should be saved for
  1749. */
  1750. Lazarus.getExpiryTime = function(){
  1751.  
  1752.     if (Lazarus.getExtPref("expireSavedForms")){
  1753.         return (Lazarus.getExtPref("expireSavedFormsInterval") * Lazarus.getExtPref("expireSavedFormsUnit") * 60);
  1754.     }
  1755.     else {
  1756.         return 0;
  1757.     }
  1758. }
  1759.  
  1760. /**
  1761. * retrieves a list of details about a form from the database.
  1762. */
  1763. Lazarus.getFormInfo = function(form, fields){
  1764.     var formId = Lazarus.getFormId(form);
  1765.     
  1766.     var expires = Lazarus.getExpiryTime();
  1767.     var cutoffTime = (expires > 0) ? (Lazarus.timestamp() - expires) : 0;
  1768.     
  1769.     fields = fields || "*";
  1770.     //we need to get ALL the templates for this form first
  1771.     var forms = [];
  1772.     forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY savetype DESC, created DESC", formId, cutoffTime));
  1773.     //and then the autosaves
  1774.     var maxAutoSaves = Lazarus.getExtPref("maxAutosavesPerForm", 3);
  1775.     forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +") ORDER BY created DESC LIMIT ?3", formId, cutoffTime, maxAutoSaves));
  1776.     //and then add normal saved forms
  1777.     var maxForms = Lazarus.getExtPref("maxSavesPerForm", 10);
  1778.     forms = forms.concat(Lazarus.db.rs("SELECT "+ fields +" FROM forms WHERE formid = ?1 AND created >= ?2 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL +" ORDER BY created DESC LIMIT ?3", formId, cutoffTime, maxForms));
  1779.     return forms;
  1780. }
  1781.  
  1782.  
  1783. /**
  1784. * return a fieldInfo object filled with information about the given field
  1785. */
  1786. Lazarus.fieldInfo = function(ele){
  1787.  
  1788.     var getPassword = Lazarus.getExtPref("savePasswordFields");
  1789.     var getHidden = Lazarus.getExtPref("saveHiddenFields");
  1790.  
  1791.     var info = {};
  1792.     info.name = ele.getAttribute("name");
  1793.     info.type = Lazarus.getElementType(ele);
  1794.     info.value = Lazarus.getElementValue(ele);
  1795.     if (info.type == "password" && !getPassword){
  1796.         info.value = null;
  1797.     }
  1798.     if (info.type == "hidden" && !getHidden){
  1799.         info.value = null;
  1800.     }
  1801.     
  1802.     switch (info.type){
  1803.         case "text":
  1804.         case "textarea":
  1805.         case "file":
  1806.         case "password":
  1807.         case "iframe":
  1808.             if (info.value && Lazarus.trim(info.value)){
  1809.                 info.text = Lazarus.trim(info.value);
  1810.             }
  1811.             break;
  1812.         
  1813.         default:
  1814.     }
  1815.     
  1816.     return info;
  1817. }
  1818.  
  1819. /**
  1820. * clean HTML tags and entities from an html string
  1821. */
  1822. Lazarus.cleanText = function(text){
  1823.     return text.replace(/<[^>]*>/g, ' ').replace(/&\w+;?/g, ' ').replace(/ +/g, ' ');
  1824. }
  1825.  
  1826. /**
  1827. * return a formInfo object filled with details about a form
  1828. */
  1829. Lazarus.formInfo = function(form){
  1830.  
  1831.     var info = {};
  1832.     info.version = Lazarus.FORM_INFO_VERSION;
  1833.     info.formid = Lazarus.getFormId(form);
  1834.     info.action = Lazarus.getUrlPage(form.action || form.ownerDocument.URL);
  1835.     info.origURL = form.ownerDocument.URL;
  1836.     info.domain = Lazarus.getDomainFromElement(form);
  1837.     info.method = (form.method && form.method.toLowerCase()) == "post" ? "post" : "get";
  1838.     info.enctype = form.enctype ? form.enctype.toLowerCase() : '';
  1839.     info.fields = {};
  1840.     info.formtext = [];
  1841.     info.textLen = 0;
  1842.     for (var i=0; i<form.elements.length; i++){
  1843.         var ele = form.elements[i];
  1844.         var name = ele.getAttribute("name");
  1845.         if (name){
  1846.             switch (Lazarus.getElementType(ele)){
  1847.                 case "text":
  1848.                 case "textarea":
  1849.                 case "file":
  1850.                 case "radio":
  1851.                 case "checkbox":
  1852.                 case "select":
  1853.                 case "password":
  1854.                 case "hidden": 
  1855.                     var fieldInfo = Lazarus.fieldInfo(ele);
  1856.                     info.fields[name] = info.fields[name] || [];
  1857.                     info.fields[name].push(fieldInfo);
  1858.                     
  1859.                     if (fieldInfo.text){
  1860.                         info.formtext.push(fieldInfo.text);
  1861.                         info.textLen += fieldInfo.text.length;
  1862.                     }
  1863.                     break;
  1864.                     
  1865.                 default:
  1866.                     //ignore all other types
  1867.             }
  1868.         }
  1869.         //support for ajax textareas
  1870.         else if (form.isTextarea && Lazarus.getElementType(ele) == "textarea"){
  1871.             var fieldInfo = Lazarus.fieldInfo(ele);
  1872.             info.isTextarea = true;
  1873.             info.fields["textarea"] = [fieldInfo];
  1874.             if (fieldInfo.text){
  1875.                 info.formtext.push(fieldInfo.text);
  1876.                 info.textLen += fieldInfo.text.length;
  1877.             }
  1878.         }
  1879.         //support for ajax textareas
  1880.         else if (form.isIframe && Lazarus.getElementType(ele) == "iframe"){
  1881.             var fieldInfo = Lazarus.fieldInfo(ele);
  1882.             info.isIframe = true;
  1883.             info.fields["iframe"] = [fieldInfo];
  1884.             if (fieldInfo.text){
  1885.                 info.formtext.push(fieldInfo.text);
  1886.                 info.textLen += fieldInfo.text.length;
  1887.             }
  1888.         }
  1889.     }
  1890.     //we need to add any WYSIWYG iframes into the form as well
  1891.     var iframes = Lazarus.getEditableIframes(form);
  1892.     if (iframes.length > 0){
  1893.         info.fields[Lazarus.IFRAME_NAME] = [];
  1894.         info.isIframe = true;        for (var i=0; i<iframes.length; i++){
  1895.             var name = Lazarus.IFRAME_NAME;
  1896.             var fieldInfo = Lazarus.fieldInfo(iframes[i]);
  1897.             
  1898.             info.fields[name] = info.fields[name] || [];
  1899.             info.fields[name].push(fieldInfo);
  1900.             
  1901.             if (fieldInfo.text){
  1902.                 info.formtext.push(fieldInfo.text);
  1903.                 info.textLen += fieldInfo.text.length;
  1904.             }
  1905.         }
  1906.     }
  1907.         
  1908.         info.formtext = Lazarus.trim(info.formtext.join("\n\n"));
  1909.     
  1910.     return info;
  1911. }
  1912.  
  1913.  
  1914. /**
  1915. * return an array of editable iframes found within the given node
  1916. */
  1917. Lazarus.getEditableIframes = function(ele){
  1918.     var editableIframes = [];
  1919.     if (ele && ele.getElementsByTagName){
  1920.         var iframes = ele.getElementsByTagName('iframe');
  1921.         
  1922.         for (var i=0; i<iframes.length; i++){
  1923.             if (Lazarus.isEditableDoc(iframes[i].contentWindow.document)){
  1924.                 editableIframes.push(iframes[i]);
  1925.             }
  1926.             //better get iframes within the iframes as well
  1927.             if (iframes[i].contentWindow.document.body){
  1928.                 editableIframes = editableIframes.concat(Lazarus.getEditableIframes(iframes[i].contentWindow.document.body));
  1929.             }
  1930.         }
  1931.     }
  1932.     return editableIframes;
  1933. }
  1934.  
  1935. /**
  1936. * return TRUE if the given document is in edit mode
  1937. */
  1938. Lazarus.isEditableDoc = function(doc){
  1939.     return (doc && (doc.designMode == "on" || (doc.body && doc.body.contentEditable === "true")));
  1940. }
  1941.  
  1942. /**
  1943. * return the base domain (wikipedia.org) given a full domain (en.wikipedia.org)
  1944. */
  1945. Lazarus.getBaseDomain = function(domain){
  1946.  
  1947.     //if domain is an ip address return that 
  1948.     var regexIp = /\d+\.\d+\.\d+\.\d+$/
  1949.     var m = domain.match(regexIp);
  1950.     if (m){return m[0]}
  1951.  
  1952.     //known top level domains (TLDs) including country specific ones
  1953.     
  1954.     //http://en.wikipedia.org/wiki/Country_code_top-level_domain
  1955.     //all domains *should* either end in a 2 letter tld (google.co.nz, google.com.au) or none (google.com) for american sites
  1956.     //that should be preceeded by the generic tld
  1957.     //and then the basedomain
  1958.     var regex = /[^\.]+\.[^\.]+(\.\w{2})?$/;
  1959.     var m = domain.match(regex);
  1960.     //if we cant figure it out then return the whole domain
  1961.     return m ? m[0] : domain;        
  1962. }
  1963.  
  1964.  
  1965.  
  1966. /**
  1967. * restores a form to the given state
  1968. */
  1969. Lazarus.restoreForm = function(form, formInfo){
  1970.  
  1971.     //attempt to restore the saved fields for this form
  1972.     var restorePassword = Lazarus.getExtPref("savePasswordFields");   
  1973.     var restoreHidden = Lazarus.getExtPref("saveHiddenFields"); 
  1974.     
  1975.     //try and recover as many fields as possible for this form.
  1976.     var iRestored = 0;
  1977.     var iNamedElements = 0;
  1978.     var formFields = {};
  1979.     for (var i=0; i<form.elements.length; i++){
  1980.         var ele = form.elements[i];
  1981.         var name = ele.getAttribute("name");
  1982.         var eleType = Lazarus.getElementType(ele);
  1983.         var eleValue = Lazarus.getElementValue(ele);
  1984.         
  1985.         if (name){
  1986.             switch (eleType){
  1987.                 case "text":
  1988.                 case "textarea":
  1989.                 case "file":
  1990.                 case "radio":
  1991.                 case "checkbox":
  1992.                 case "select":
  1993.                 case "password":
  1994.                 case "hidden":
  1995.                     //try to restore this element
  1996.                     iNamedElements++;
  1997.                     //do we have an match for this element in our saved form 
  1998.                     if (formInfo.fields[name]){
  1999.                         for (var j=0; j<formInfo.fields[name].length; j++){
  2000.                             var info = formInfo.fields[name][j];
  2001.                             if (!info.restored && info.type == eleType){
  2002.                                 switch (eleType){
  2003.                                     case "text":
  2004.                                     case "textarea":
  2005.                                     case "file":
  2006.                                     case "select":
  2007.                                         Lazarus.setElementValue(ele, info.value);
  2008.                                         iRestored++;
  2009.                                         break;
  2010.                                         
  2011.                                     case "radio":
  2012.                                     case "checkbox":
  2013.                                         if (info.value && (eleValue.valueAttr == info.value.valueAttr)){
  2014.                                             Lazarus.setElementValue(ele, info.value.checked);
  2015.                                             iRestored++;
  2016.                                         }
  2017.                                         break;
  2018.                                         
  2019.                                     case "password":
  2020.                                         if (restorePassword){
  2021.                                             Lazarus.setElementValue(ele, info.value);
  2022.                                         }
  2023.                                         iRestored++;
  2024.                                         break;
  2025.                                         
  2026.                                     case "hidden":
  2027.                                         if (restoreHidden){
  2028.                                             Lazarus.setElementValue(ele, info.value);
  2029.                                         }
  2030.                                         iRestored++;
  2031.                                         break;
  2032.                                     
  2033.                                     default:
  2034.                                         //no need to restore 
  2035.                                         iRestored++;
  2036.                                 }
  2037.                             }
  2038.                         }
  2039.                     }
  2040.                 default:
  2041.                     //ignore other types of elements
  2042.             }
  2043.         }
  2044.         else if (form.isTextarea && Lazarus.isTextarea(ele)){
  2045.             var info = formInfo.fields["textarea"][0];
  2046.             Lazarus.setElementValue(ele, info.value);
  2047.             iRestored++;
  2048.             iNamedElements++;
  2049.         }
  2050.         else if (form.isIframe && ele.tagName == "iframe"){
  2051.             var info = formInfo.fields["iframe"][0];
  2052.             Lazarus.setElementValue(ele, info.value);
  2053.             iRestored++;
  2054.             iNamedElements++;
  2055.         }
  2056.     }
  2057.     //try and restore any WYSIWYG editors as well
  2058.     var iframes = Lazarus.getEditableIframes(form);
  2059.     if (iframes && formInfo.fields[Lazarus.IFRAME_NAME]){
  2060.         for (var i=0; i<iframes.length; i++){
  2061.             var info = formInfo.fields[Lazarus.IFRAME_NAME][i];
  2062.             if (info){
  2063.                 Lazarus.setElementValue(iframes[i], info.value);
  2064.             }
  2065.         }
  2066.     }
  2067.     
  2068.     
  2069.     //check for errors
  2070.     if (iRestored == 0){
  2071.         alert(Lazarus.getString("error.restore.none"));
  2072.         return false;
  2073.     }
  2074.     //tell them if we couldn't restore some fields
  2075.     else if (iRestored < iNamedElements){
  2076.         //only show the partial restore message if the form is not an old style form
  2077.         if (formInfo.version && formInfo.version >= 1){
  2078.             alert(Lazarus.getString("error.restore.partial"));
  2079.         }
  2080.         return (iRestored / iNamedElements);
  2081.     }
  2082.     else {
  2083.         return true;
  2084.     }
  2085. }
  2086.  
  2087. /**
  2088. * handle the onselectmenuitem event
  2089. */
  2090. Lazarus.onRestoreFormMenuItem = function(menuitem){
  2091.  
  2092.     if (!Lazarus.canDecrypt()){
  2093.         Lazarus.debug("Unable to restore form, password required");
  2094.         Lazarus.showNotificationBox("password-required"); 
  2095.         return;
  2096.     }
  2097.     
  2098.     var id = parseInt(menuitem.getAttribute("lazarus-forms-id"));
  2099.     
  2100.     Lazarus.debug("Attempting to restore form "+ id);
  2101.     var form = Lazarus.findFormFromElement(gContextMenu.target, "form");
  2102.     if (form){
  2103.         var row = Lazarus.db.getRow("SELECT * FROM forms WHERE id = ?1", id);
  2104.         if (row){
  2105.             var formInfo;
  2106.             try {
  2107.                 formInfo = Lazarus.JSON.decode(Lazarus.decrypt(row["forminfo"]));
  2108.             }
  2109.             catch(e){
  2110.                 Lazarus.error(e);
  2111.             }
  2112.             
  2113.             if (formInfo){
  2114.                 //if we fully restore a form, then show the "donate" popup
  2115.                 if (Lazarus.restoreForm(form, formInfo) === true){
  2116.                     Lazarus.incPref("extensions.lazarus.restoreFormCount");
  2117.                     if ((formInfo.textLen > Lazarus.MIN_TEXT_NEEDED_TO_SHOW_NOTIFICATION) && Lazarus.getPref("extensions.lazarus.showDonateNotification") && Lazarus.getPref("extensions.lazarus.restoreFormCount", 0) > 3){
  2118.                         var msgId = Math.floor(Math.random() *  4);
  2119.                         Lazarus.showNotificationBox("form-restored", Lazarus.getFormRestoredNotificationText(formInfo.textLen, msgId), "msgid-"+ msgId);                        
  2120.                     }
  2121.                 }
  2122.             }
  2123.             else {
  2124.                 alert(Lazarus.getString("error.form.db.corrupt"));
  2125.             }
  2126.         }
  2127.         else {
  2128.             alert(Lazarus.getString("error.form.not.found"));
  2129.         }
  2130.     }
  2131.     else {
  2132.         alert(Lazarus.getString("error.form.object.not.found"));
  2133.     }
  2134. }
  2135.  
  2136. /**
  2137. * return TRUE if form is a form we should save
  2138. */
  2139. Lazarus.shouldSaveForm = function(form){
  2140.     //only save forms on file/http/https sites
  2141.     var doc = form.ownerDocument;
  2142.     if (doc && doc instanceof HTMLDocument && doc.URL && /^(file|http|https):/.test(doc.URL) && !Lazarus.isPageDisabled(doc.URL) && !Lazarus.isDisabledByPrivateBrowsing()){
  2143.         return (Lazarus.isSearchForm(form)) ? Lazarus.getPref("extensions.lazarus.saveSearchForms") : true;
  2144.     }
  2145.     else {
  2146.         return false;
  2147.     }
  2148. }
  2149.  
  2150. /**
  2151. * return TRUE if we should be saving this info
  2152. */
  2153. Lazarus.shouldSaveEditorInfo = function(info){
  2154.         if (Lazarus.isPageDisabled(info.url)){
  2155.             return false;
  2156.         }
  2157.         else if (Lazarus.isDisabledByPrivateBrowsing()){
  2158.             return false;
  2159.         }
  2160.         else {
  2161.             return true;
  2162.         }
  2163. }
  2164.  
  2165.  
  2166.  
  2167. Lazarus.disabledDomains = null;
  2168.  
  2169. Lazarus.isPageDisabled = function(url){
  2170.     if (!url && content.document && content.document.URL){
  2171.         url = content.document.URL;
  2172.     }
  2173.     if (url){
  2174.         var domainId = Lazarus.urlToDomainId(url);
  2175.         if (domainId){
  2176.             var domains = Lazarus.getDisabledDomains();            
  2177.             return (typeof domains[domainId] !== "undefined") ? domains[domainId] : false;
  2178.         }
  2179.     }
  2180.     return false;
  2181. }
  2182.  
  2183. /**
  2184. * return TRUE if lazarus can save forms from this site
  2185. * return FALSE for non-valid sites (eg about:config, chrome://... etc..)
  2186. */
  2187. Lazarus.isValidSite = function(url){
  2188.     //default to the current documents url
  2189.     if (!url && content.document && content.document.URL){
  2190.         url = content.document.URL;
  2191.     }
  2192.     return (url && Lazarus.urlToDomainId(url)) ? true : false;
  2193. }
  2194.  
  2195. Lazarus.getDisabledDomains = function(){
  2196.     var domainlist = Lazarus.getPref('extensions.lazarus.domainBlacklist', '');
  2197.     
  2198.     if (!Lazarus.disabledDomains || Lazarus.disabledDomains['__origList__'] != domainlist){
  2199.         //rebuild the list
  2200.         Lazarus.disabledDomains = {
  2201.             '__origList__': domainlist
  2202.         }
  2203.         var domains = domainlist.split(/\s*,\s*/g);
  2204.         for (var i=0; i<domains.length; i++){
  2205.             Lazarus.disabledDomains[domains[i]] = true;
  2206.         }    
  2207.     }
  2208.     return Lazarus.disabledDomains;
  2209. }
  2210.  
  2211. Lazarus.saveDisabledDomains = function(domainsTable){
  2212.     //convert the table back into an array and save it.
  2213.     var domains = [];
  2214.     for(var domain in domainsTable){
  2215.         if (domainsTable[domain] && /^\w+:/.test(domain)){
  2216.             domains.push(domain);
  2217.         }
  2218.     }
  2219.  
  2220.     return Lazarus.setPref('extensions.lazarus.domainBlacklist', domains.join(','));
  2221. }
  2222.  
  2223.  
  2224. Lazarus.enableForCurrentDomain = function(){
  2225.     //fetch the current domain, and disable Lazarus
  2226.     if (content.document && content.document.URL){
  2227.         var domainId = Lazarus.urlToDomainId(content.document.URL);
  2228.         //and add to disabled domains list.
  2229.         
  2230.         var domains = Lazarus.getDisabledDomains();
  2231.         if (!domains[domainId]){
  2232.             domains[domainId] = true;
  2233.             Lazarus.saveDisabledDomains(domains);
  2234.             Lazarus.debug('Domain disabled ['+ domainId +']');
  2235.         }
  2236.         else {
  2237.             //should already be disabled!
  2238.             Lazarus.warning('Domain is already disabled ['+ domainId +']');
  2239.         }
  2240.        
  2241.         //and refresh the statusbar icon
  2242.         Lazarus.refreshIcon();
  2243.     }
  2244.     else {
  2245.         alert(Lazarus.getString("error.no.document"));
  2246.     }
  2247. }
  2248.  
  2249.  
  2250. /**
  2251. * enables or disabled on the current website
  2252. */
  2253. Lazarus.toggleCurrentDomain = function(enable){
  2254.     //fetch the current domain, and disable Lazarus
  2255.     if (content.document && content.document.URL){
  2256.         var domains = Lazarus.getDisabledDomains();
  2257.         var domainId = Lazarus.urlToDomainId(content.document.URL);
  2258.         
  2259.         if (domainId){
  2260.             //add/remove from disabled domains list
  2261.             domains[domainId] = enable ? false : true;
  2262.             Lazarus.saveDisabledDomains(domains);
  2263.             Lazarus.debug('Domain ['+ domainId +'] enabled = '+ enable);
  2264.             //and refresh the statusbar icon
  2265.             Lazarus.refreshIcon();
  2266.         }
  2267.         else {
  2268.             Lazarus.error("Failed to extract domainId from URL ["+ content.document.URL +"]");
  2269.         }
  2270.     }
  2271.     else {
  2272.         alert(Lazarus.getString("error.no.document"));
  2273.     }
  2274. }
  2275.  
  2276.  
  2277. Lazarus.onStatusbarMenuShowing = function(popupmenu){
  2278.     var validSite = Lazarus.isValidSite();
  2279.     var siteDisabled = Lazarus.isPageDisabled();
  2280.     var isPrivate = Lazarus.isDisabledByPrivateBrowsing();
  2281.     
  2282.     Lazarus.$('lazarus-statusbar-menuitem-toggledomain-separator').hidden = !(validSite && !isPrivate);
  2283.     Lazarus.$('lazarus-statusbar-menuitem-enablefordomain').hidden = !(validSite && siteDisabled && !isPrivate);
  2284.     Lazarus.$('lazarus-statusbar-menuitem-disablefordomain').hidden = !(validSite && !siteDisabled && !isPrivate);
  2285.     Lazarus.$('lazarus-statusbar-menuitem-logout').hidden = !(Lazarus.Crypto.isPasswordEntered);
  2286. }
  2287.  
  2288. /**
  2289. * return a website identifier
  2290. * http://google.com is different from https://google.com
  2291. */
  2292. Lazarus.urlToDomainId = function(url){
  2293.     var uri = Lazarus.urlToURI(url);
  2294.     return (uri && uri.scheme && uri.host && (/^(http|https|file)$/.test(uri.scheme))) ? (uri.scheme +":"+ ((uri.port > -1 && uri.port != 80) ? uri.port : '') +'//'+ Lazarus.getBaseDomain(uri.host) +'/') : '';   
  2295. }
  2296.  
  2297.  
  2298. /**
  2299. * return TRUE if form appears to be a search form
  2300. */
  2301. Lazarus.isSearchForm = function(form){
  2302.     //search forms are forms that have exactly one textbox, and may contain a number of other non-text fields
  2303.     var MAX_TEXT_FIELDS = 1;
  2304.     var MAX_NON_TEXT_FIELDS = 5;
  2305.     
  2306.     var iTextFields = 0;
  2307.     var iNonTextFields = 0;
  2308.     
  2309.     for (var i=0; i<form.elements.length; i++){
  2310.         var ele = form.elements[i];
  2311.         switch(Lazarus.getElementType(ele)){
  2312.             case "text":
  2313.                 iTextFields++;
  2314.                 break;
  2315.             
  2316.             case "radio":
  2317.             case "checkbox":
  2318.             case "select":
  2319.                 iNonTextFields++;
  2320.                 break;
  2321.                 
  2322.             case "file":    
  2323.             case "password":
  2324.             case "textarea":
  2325.                 return false;
  2326.                         
  2327.             case "hidden":
  2328.             case "submit":
  2329.             case "reset":
  2330.             case "button":
  2331.             case "image":
  2332.             default:
  2333.                 //ignore all other types
  2334.         }
  2335.     }
  2336.     //NOTE: we MUST have a single text field
  2337.     return  (iTextFields == MAX_TEXT_FIELDS && iNonTextFields <= MAX_NON_TEXT_FIELDS);
  2338. }
  2339.  
  2340.  
  2341. /**
  2342. * check a submit event, and saves form details if the event is from a valid form
  2343. */
  2344. Lazarus.onFormSubmit = function(evt){
  2345.     var form = Lazarus.findFormFromElement(evt.target);
  2346.     //is the form on an html document?
  2347.     if (form && form.ownerDocument instanceof HTMLDocument){
  2348.         Lazarus.stopAutoSaveTimer();
  2349.         Lazarus.saveForm(form, Lazarus.FORM_TYPE_NORMAL);
  2350.     }
  2351. }
  2352.  
  2353. /**
  2354. * return "post" of "get" depending on the given forms method
  2355. */
  2356. Lazarus.getFormMethod = function(form){
  2357.     var method = form.getAttribute("method");
  2358.     return (method && method.toLowerCase() == "post") ? "post" : "get";
  2359. }
  2360.  
  2361. /**
  2362. * autosave a form if we change anything about it.
  2363. */
  2364. Lazarus.onFormChange = function(evt){
  2365.     var form = Lazarus.findFormFromElement(evt.target);
  2366.     
  2367.     if (form && form.ownerDocument instanceof HTMLDocument){
  2368.         //I think we should chuck a timer in here, because we might end up saving a lot.
  2369.         Lazarus.restartAutoSaveTimer(form);
  2370.     }
  2371. }
  2372.  
  2373. /**
  2374. * check a reset event, and saves form details if the event is from a valid form
  2375. */
  2376. Lazarus.onFormReset = function(evt){
  2377.     var form = Lazarus.findFormFromElement(evt.target);
  2378.     //is the form on an html document?
  2379.     if (form && form.ownerDocument instanceof HTMLDocument){
  2380.         Lazarus.stopAutoSaveTimer();
  2381.         Lazarus.saveForm(form, Lazarus.FORM_TYPE_AUTOSAVE);
  2382.     }
  2383. }
  2384.  
  2385. /**
  2386. * create a searchable index of hashed words
  2387. */
  2388. Lazarus.indexFormText = function(id, text, url){
  2389.         //and update the full text index,
  2390.         if (!Lazarus.getPref('extensions.lazarus.disableSearch')){
  2391.                 //we're going to add domain info into this as well so a user can search for domain fragments too
  2392.                 var hashedText = Lazarus.hashText(text);
  2393.                 
  2394.                 //extract the domain from the url
  2395.                 var domain = Lazarus.urlToURI(url).host;
  2396.                 
  2397.                 hashedText += ' '+ Lazarus.hashText(domain.replace(/\./g, ' '));
  2398.                 
  2399.                 Lazarus.db.exe("DELETE FROM forms_fulltext WHERE docid = ?1", id);
  2400.                 Lazarus.db.exe("INSERT INTO forms_fulltext (docid, hashed_text) VALUES (?1, ?2)", id, hashedText);
  2401.         }
  2402. }
  2403.  
  2404. /**
  2405. * save all the named elements in a form to the permanent store
  2406. */
  2407. Lazarus.saveForm = function(form, formType, templateName, autofill){
  2408.     
  2409.     autofill = autofill ? 1 : 0;
  2410.     
  2411.     if (Lazarus.shouldSaveForm(form)){
  2412.     
  2413.         templateName = templateName || '';
  2414.         
  2415.         var info = Lazarus.formInfo(form);
  2416.         
  2417.         //dont save empty forms?
  2418.         //No. what about forms with lots of radio buttons (eg multichoice surveys)    
  2419.         var infoJSON = Lazarus.JSON.encode(info)
  2420.         var encryptedInfo = Lazarus.encrypt(infoJSON);
  2421.         var encryptedText = Lazarus.encrypt(Lazarus.getMenuItemText(info.formtext));
  2422.         var encryptedFormURL = Lazarus.encrypt(Lazarus.getFormURL(form));
  2423.         
  2424.         var infoHash = Lazarus.generateHash(info.fields);
  2425.         
  2426.         //each form type needs to be saved differently
  2427.         switch(formType){
  2428.             case Lazarus.FORM_TYPE_AUTOSAVE:
  2429.             case Lazarus.FORM_TYPE_STALE_AUTOSAVE: 
  2430.             
  2431.                 var savedForm = Lazarus.db.getRow("SELECT id, savetype FROM forms WHERE formid = ?1 AND forminfohash = ?2 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +") LIMIT 1", info.formid, infoHash);
  2432.         
  2433.                 //add or update the current autosave
  2434.                 if (!savedForm){
  2435.                     var lastAutoSaveId = Lazarus.db.getInt("SELECT id FROM forms WHERE formid = ?1 AND savetype = "+ Lazarus.FORM_TYPE_AUTOSAVE +" ORDER BY created DESC LIMIT 1", info.formid);
  2436.                     var lastAutoSave = (lastAutoSaveId && Lazarus.lastAutoSaveForm && Lazarus.lastAutoSaveForm["formid"] == info.formid) ? Lazarus.lastAutoSaveForm : null;
  2437.                     var newId = 0;
  2438.                                         
  2439.                     if (lastAutoSave && Lazarus.shouldCreateNewAutosave(info, lastAutoSave)){
  2440.                         //if the saved form is smaller than the last saved form by a substantial amount,
  2441.                         //create a new restore point.
  2442.                         Lazarus.debug("Creating additional autosave point "+ info.formid);
  2443.                         Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formurl, text_length, savetype) \
  2444.                             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, "+ Lazarus.FORM_TYPE_AUTOSAVE +")", info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen);
  2445.                                                 newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
  2446.                     }
  2447.                     else if (lastAutoSave){
  2448.                         Lazarus.debug("Updating autosave form "+ info.formid);
  2449.                         Lazarus.db.exe("UPDATE forms SET created = ?1, forminfo = ?2, formtext = ?3, forminfohash = ?4, formurl = ?5, text_length = ?6 \
  2450.                             WHERE id = ?7", Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen, lastAutoSaveId);
  2451.                                                 newId = lastAutoSaveId;
  2452.                     }
  2453.                     else {
  2454.                         Lazarus.debug("Creating new autosave form "+ info.formid);
  2455.                         Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formurl, text_length, savetype) \
  2456.                             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, "+ Lazarus.FORM_TYPE_AUTOSAVE +")", info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, encryptedFormURL, info.textLen);
  2457.                                                 newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
  2458.                     }
  2459.                                         
  2460.                                         //update the full text search as well
  2461.                                         Lazarus.indexFormText(newId, info.formtext, info.origURL);
  2462.                                         
  2463.                     //and save this forminfo as the last autosave point
  2464.                     Lazarus.lastAutoSaveForm = info;
  2465.                 }
  2466.                 else {
  2467.                     Lazarus.debug("No need to autosave form, form already exists: "+ info.formid);
  2468.                 }
  2469.                 
  2470.                 break;
  2471.             
  2472.             case Lazarus.FORM_TYPE_NORMAL: 
  2473.                 //delete all autosaves 
  2474.                 Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND savetype IN ("+ Lazarus.FORM_TYPE_AUTOSAVE +","+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +")", info.formid));
  2475.             
  2476.                 //if this form already exists, delete it
  2477.                 Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND forminfohash = ?2 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL, info.formid, infoHash));
  2478.         
  2479.                 //and save this form
  2480.                 Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formname, formurl, text_length, savetype, autofill) \
  2481.                     VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 0)", 
  2482.                     info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, templateName, encryptedFormURL, info.textLen, formType);
  2483.                 Lazarus.debug("form saved ["+ info.formid +"]");
  2484.                                 
  2485.                                 //update the full text search as well
  2486.                                 var newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
  2487.                                 Lazarus.indexFormText(newId, info.formtext, info.origURL);
  2488.                 break;
  2489.             
  2490.             case Lazarus.FORM_TYPE_TEMPLATE: 
  2491.                 //if another template with the same name exists, delete it
  2492.                                 Lazarus.removeForms(Lazarus.db.getColumn("SELECT id FROM forms WHERE formname = ?1 AND savetype = "+ Lazarus.FORM_TYPE_TEMPLATE, templateName));
  2493.         
  2494.                 //likewise we are not allowed to have more then one autofill for the same form.
  2495.                 if (autofill){
  2496.                     Lazarus.db.exe("UPDATE forms SET autofill = 0 WHERE formid = ?1", info.formid);
  2497.                 }
  2498.                 
  2499.                 //and save this form 
  2500.                 Lazarus.db.exe("INSERT INTO forms (formid, created, forminfo, formtext, forminfohash, formname, formurl, text_length, savetype, autofill) \
  2501.                     VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", 
  2502.                     info.formid, Lazarus.timestamp(), encryptedInfo, encryptedText, infoHash, templateName, encryptedFormURL, info.textLen, formType, autofill);
  2503.                 
  2504.                                 var newId = Lazarus.db.getInt("SELECT MAX(id) FROM forms");
  2505.                                 Lazarus.indexFormText(newId, info.formtext, info.origURL);
  2506.                                 Lazarus.debug("template saved ["+ info.formid +"]");
  2507.                 break;
  2508.                     
  2509.             default:
  2510.                 Lazarus.error(Error("Unknown form type ["+ row["formtype"] +"]"));
  2511.                 
  2512.         }
  2513.         Lazarus.startCleanupTimer();
  2514.     }
  2515.     else {
  2516.         Lazarus.debug("Form not saved: invalid form");
  2517.     }
  2518. }
  2519.  
  2520.  
  2521. /**
  2522. * remove forms from the database
  2523. */
  2524. Lazarus.removeForms = function(ids){
  2525.     Lazarus.debug("removing forms", ids);
  2526.     if (!ids || (Lazarus.isArray(ids) && ids.length === 0)){
  2527.         return;
  2528.     }
  2529.  
  2530.     if (!Lazarus.isArray(ids)){
  2531.         ids = [ids];
  2532.     }
  2533.     //make sure the ids are safe.
  2534.     for(var i=0; i<ids.length; i++){
  2535.         ids[i] = parseInt(ids[i]);
  2536.     } 
  2537.     Lazarus.db.exe("DELETE FROM forms WHERE id IN ("+ ids.join(",") +")");
  2538.     Lazarus.db.exe("DELETE FROM forms_fulltext WHERE docid IN ("+ ids.join(",") +")"); 
  2539.     
  2540.     Lazarus.debug(ids.length +" forms removed");
  2541. }
  2542.  
  2543.  
  2544. /**
  2545. * return the URL of the page this form is currently on, but leave any query info
  2546. */
  2547. Lazarus.getFormURL = function(form){
  2548.     //we'll strip any anchor tags from the URL
  2549.     return form.ownerDocument.URL.replace(/#.*/, '');
  2550. }
  2551.  
  2552. /**
  2553. * return an integer representing how different one form is from another
  2554. * 0 being identical
  2555. * 100 being 100 characters/values are different
  2556. */
  2557. Lazarus.shouldCreateNewAutosave  = function(newForm, oldForm){
  2558.  
  2559.     //number of characters to ignore when calculating if the form has changed substantially
  2560.     var TEXT_DIFFERENCE = 32; //
  2561.  
  2562.     if (oldForm.formtext.length > (TEXT_DIFFERENCE * 2)){
  2563.         var str = oldForm.formtext.substr(0, oldForm.formtext.length - TEXT_DIFFERENCE);
  2564.         //if new form is a continuation of old form (ie the first (X - TEXT_DIFFERENCE) characters are still the same), 
  2565.         //then overwrite current autosave,
  2566.         //otherwise create a new autosave.
  2567.         return (newForm.formtext.indexOf(str) == -1);
  2568.     }
  2569.     //form contains little text, overwrite the current autosave
  2570.     else {
  2571.         return false;
  2572.     }     
  2573. }
  2574.  
  2575. /**
  2576. * automatically save the form the user is working on.
  2577. */
  2578. Lazarus.autoSaveForm = function(){
  2579.     //does the form still exist?
  2580.     if (Lazarus.currAutoSaveForm && !Lazarus.isContextMenuShowing){
  2581.         Lazarus.saveForm(Lazarus.currAutoSaveForm, Lazarus.FORM_TYPE_AUTOSAVE);
  2582.     }
  2583. }
  2584.  
  2585. /**
  2586. * start the cleanup timer
  2587. */
  2588. Lazarus.startCleanupTimer = function(){
  2589.     
  2590.     Lazarus.stopCleanupTimer();
  2591.     
  2592.     if (Lazarus.getExpiryTime()){    
  2593.         Lazarus.cleanupSavedFormsTimer = setInterval(Lazarus.cleanupSavedForms, 1000 * 60);
  2594.     }
  2595. }
  2596.  
  2597. /**
  2598. * stop the cleanup timer
  2599. */
  2600. Lazarus.stopCleanupTimer = function(){
  2601.     if (Lazarus.cleanupSavedFormsTimer){
  2602.         clearInterval(Lazarus.cleanupSavedFormsTimer);
  2603.     }
  2604. }
  2605.  
  2606. /**
  2607. * remove old forms from the database
  2608. */
  2609. Lazarus.cleanupSavedForms = function(){
  2610.  
  2611.     //dont cleanup any forms if a user might be trying to retore them
  2612.     if (Lazarus.isContextMenuShowing){return}
  2613.     
  2614.     var expires = Lazarus.getExpiryTime();
  2615.  
  2616.     if (expires > 0){
  2617.         //add a couple of minutes to the expiry time, so we don't accidentally 
  2618.         //remove a form whilst we are current restoring it.
  2619.         var cutoffTime = Lazarus.timestamp() - (expires + 120);
  2620.         var ids = Lazarus.db.getColumn("SELECT id FROM forms WHERE created < ?1 AND savetype != "+ Lazarus.FORM_TYPE_TEMPLATE, cutoffTime);
  2621.                 Lazarus.removeForms(ids);
  2622.                 
  2623.                 var ids = Lazarus.db.getColumn("SELECT id FROM textdata WHERE created < ?1", cutoffTime);
  2624.         if (ids.length > 0){
  2625.             Lazarus.db.exe("DELETE FROM textdata WHERE id IN ("+ ids.join(",") +")");
  2626.             Lazarus.db.exe("DELETE FROM textdata_fulltext WHERE docid IN ("+ ids.join(",") +")"); 
  2627.         }
  2628.     }
  2629.     else {
  2630.         Lazarus.stopCleanupTimer();
  2631.     }
  2632. }
  2633.  
  2634. /**
  2635. * return an identifier for a url
  2636. */
  2637. Lazarus.getUrlPage = function(url){
  2638.     var uri = Lazarus.urlToURI(url);
  2639.     if (uri){
  2640.         return uri.scheme +"://"+ uri.hostPort + uri.path.replace(/[\?#].*$/, '');
  2641.     }
  2642.     //for testing on file systems
  2643.     else if ((Lazarus.getExtPref("debugMode") >= 3) && form.action.match(/^file:\/\//i)){
  2644.         return "file://just/testing";
  2645.     }
  2646.     else {
  2647.         return null;
  2648.     }
  2649. }
  2650.  
  2651. /**
  2652. * return the user editable fields within a form.
  2653. */
  2654. Lazarus.getEditableFields = function(form){
  2655.     var fields = [];
  2656.     var saveHidden = Lazarus.getExtPref("saveHiddenFields");
  2657.     var savePassword = Lazarus.getExtPref("savePasswordFields");
  2658.     
  2659.     for (var i=0; i<form.elements.length; i++){
  2660.         var ele = form.elements[i];
  2661.         //ignore fields with no name (they dont get submitted to the server)
  2662.         if (ele.getAttribute("name")){
  2663.             switch(Lazarus.getElementType(ele)){
  2664.                 case "text":
  2665.                 case "textarea":
  2666.                 case "file":
  2667.                 case "radio":
  2668.                 case "checkbox":
  2669.                 case "select":
  2670.                     fields.push(ele);
  2671.                     break;
  2672.                 
  2673.                 case "password":
  2674.                     if (savePassword){
  2675.                         fields.push(ele);
  2676.                     }
  2677.                     break;
  2678.                     
  2679.                 case "hidden":
  2680.                     if (saveHidden){
  2681.                         fields.push(ele);
  2682.                     }
  2683.                     break;
  2684.                     
  2685.                 //ignore buttons
  2686.                 case "submit":
  2687.                 case "reset":
  2688.                 case "button":
  2689.                 case "image":
  2690.                 //and unknown elements
  2691.                 default:
  2692.                     //unknown element type
  2693.                     break;
  2694.             }
  2695.         }
  2696.     }
  2697.     return fields;
  2698. }
  2699.  
  2700.  
  2701. /**
  2702. * return TRUE if element is a textarea
  2703. */
  2704. Lazarus.isTextarea = function(ele){
  2705.     return (ele && ele.nodeName && ele.nodeName.toLowerCase() == "textarea");
  2706. }
  2707.  
  2708.  
  2709. /**
  2710. * return TRUE if element is an iframe
  2711. */
  2712. Lazarus.isIframe = function(ele){
  2713.     return (ele && ele.nodeName && ele.nodeName.toLowerCase() == "iframe");
  2714. }
  2715.  
  2716. /**
  2717. * generate a formid for this form
  2718. */
  2719. Lazarus.getFormId = function(form){ 
  2720.  
  2721.     //if this form is from the Lazarus Form Recovery page, then use the id specified in the form
  2722.     if (Lazarus.isDocRecoveryForm(form.ownerDocument)){
  2723.         return form.getAttribute("lazarus-form-id");
  2724.     }
  2725.     
  2726.  
  2727.     //forms with no "action" attribute default to sending data to the current page
  2728.     var action = form.action || form.ownerDocument.URL;
  2729.     var uri = Lazarus.urlToURI(action);
  2730.     
  2731.     var formId = (uri && uri.host) ? Lazarus.getBaseDomain(uri.host) : '';
  2732.     //debugging (on local file system)
  2733.     if (!formId && (Lazarus.getExtPref("debugMode") >= 3) && action.match(/^file:\/\//i)){
  2734.         formId = "file://just/testing";
  2735.     }
  2736.     
  2737.     //and point to the same place 
  2738.     if (formId){
  2739.  
  2740.         //support for ajax textareas
  2741.         if (form.isTextarea){
  2742.             formId += "<textarea>";            
  2743.         }
  2744.         //and iframes not contained within forms
  2745.         else if (form.isIframe){
  2746.             formId += "<iframe>";            
  2747.         }
  2748.         //#7: Not saving trac "comment" forms
  2749.         //sometimes forms will add additional hidden fields 
  2750.         
  2751.         //if the form has a name, formId , or class we'll add that as well.
  2752.         //no not class. If a form has an error the classname might be changed to highlight 
  2753.         //the error to the user 
  2754.         
  2755.         //we will generate an formId from the editable fields name property
  2756.         //only fields with a name property are submitted.
  2757.         else if (form.name || form.id){
  2758.             formId += form.name ? ("@"+ form.name) : "";
  2759.             //KLUDGE: gmails email form changes the id of the form every time you start a new email
  2760.             formId += form.id && (form.ownerDocument.domain.indexOf("google") == -1) ? ("#"+ form.id) : "";
  2761.         }
  2762.         //fall back to using an identifier generated form the elements within the page
  2763.         else {
  2764.             var names = [];
  2765.             for (var i=0; i<form.elements.length; i++){
  2766.                 var ele = form.elements[i];
  2767.                 var name = ele.getAttribute("name");
  2768.                 if (name){
  2769.                     switch(Lazarus.getElementType(ele)){
  2770.                         case "text":
  2771.                         case "textarea":
  2772.                         case "file":
  2773.                         case "radio":
  2774.                         case "checkbox":
  2775.                         case "select": 
  2776.                             names.push(name.toLowerCase());
  2777.                     }
  2778.                 }
  2779.             }
  2780.             
  2781.             //sort and remove duplicate names
  2782.             names.sort();
  2783.             names = Lazarus.arrayUnique(names);
  2784.             
  2785.             formId += "["+ names.join(",") +"]";
  2786.         }
  2787.         
  2788.         //#48: including the domain within the formId constitutes a privacy risk.
  2789.         formId = Lazarus.md5(formId);
  2790.         return formId;
  2791.     }
  2792.     else {
  2793.         Lazarus.warning("Lazarus: Unable to convert url to uri ["+ action +"]");
  2794.         return '';
  2795.     }
  2796. }
  2797.  
  2798. /**
  2799. * return a new array containing members of the given array with no duplicates
  2800. */
  2801. Lazarus.arrayUnique = function(arr){
  2802.     var newArr = [];
  2803.     for (var i=0; i<arr.length; i++){
  2804.         if (!Lazarus.inArray(arr[i], newArr)){
  2805.             newArr.push(arr[i]);
  2806.         }
  2807.     }
  2808.     return newArr;
  2809. }
  2810.  
  2811. /**
  2812. * return a user friendly string stating the elapsed time in seconds, minutes, hours or days
  2813. */
  2814. Lazarus.getTimeString = function(sec){
  2815.  
  2816.     if (sec < 1){sec = 1}
  2817.     
  2818.     var units = {
  2819.         "day" : 60 * 60 * 24,
  2820.         "hour" : 60 * 60,
  2821.         "minute": 60,
  2822.         "second": 1
  2823.     }
  2824.     
  2825.     for(var unit in units){
  2826.         if (sec >= units[unit]){
  2827.             var numUnits = Math.floor(sec / units[unit]);
  2828.             if (numUnits == 1){
  2829.                 return Lazarus.getString("elapsed."+ unit);
  2830.             }
  2831.             else {
  2832.                 return Lazarus.getString("elapsed."+ unit +"s", numUnits);
  2833.             }
  2834.         }
  2835.     }
  2836.     //should never get here
  2837.     return Lazarus.getString("elapsed.second");
  2838. }
  2839.  
  2840. /**
  2841. * returns an elements value
  2842. */
  2843. Lazarus.getElementValue = function(ele){
  2844.     switch(Lazarus.getElementType(ele)){
  2845.         //text fields
  2846.         case "text":
  2847.         case "password":
  2848.         case "textarea":
  2849.         case "file":
  2850.         case "hidden":
  2851.             return ele.value;
  2852.             
  2853.         case "radio":
  2854.         case "checkbox":
  2855.             return {
  2856.                 "valueAttr": ele.value,
  2857.                 "checked": ele.checked
  2858.             }
  2859.             
  2860.         //buttons
  2861.         case "submit":
  2862.         case "reset":
  2863.         case "button":
  2864.         case "image":
  2865.             return ele.value;
  2866.         
  2867.         case "select":
  2868.             //select boxes have the option to allow multiple selections
  2869.             var selected = [];
  2870.             if (ele.options){
  2871.                 for (var i=0; i<ele.options.length; i++){
  2872.                     if (ele.options[i].selected){
  2873.                         selected.push(ele.options[i].value);
  2874.                     }
  2875.                 }
  2876.             }
  2877.             return selected;
  2878.         
  2879.         case "iframe":
  2880.             var doc = ele.contentWindow.document;
  2881.             return (doc && doc.body && doc.body.innerHTML) ? doc.body.innerHTML : '';
  2882.             
  2883.         default:
  2884.             //unknown element type
  2885.             return null;
  2886.     }
  2887. }
  2888.  
  2889. /**
  2890. * returns an elements value
  2891. */
  2892. Lazarus.setElementValue = function(ele, value){
  2893.     switch(Lazarus.getElementType(ele)){
  2894.         //text fields
  2895.         case "text":
  2896.         case "password":
  2897.         case "textarea":
  2898.         case "file":
  2899.         case "hidden":
  2900.             ele.value = value;
  2901.             break;
  2902.             
  2903.         case "radio":
  2904.         case "checkbox":
  2905.             ele.checked = value;  
  2906.             break;
  2907.             
  2908.         //buttons
  2909.         case "submit":
  2910.         case "reset":
  2911.         case "button":
  2912.         case "image":
  2913.             ele.value = value;
  2914.             break;
  2915.         
  2916.         case "select":
  2917.             //select boxes have the option to allow multiple selections
  2918.             var selected = [];
  2919.             if (ele.options){
  2920.                 //bugfix: RT: 101284
  2921.                 //inArray is taking too long for large (10,000+) select boxes, so we'll convert the values array into 
  2922.                 //a hash table instead
  2923.                 //hmmm, the problem is not actually in the inArray function
  2924.                 //but rather the line 
  2925.                 //ele.options[i].selected = table[ele.options[i].value] ? true : false;
  2926.                 // it appears that a lot goes on behind the scenes when a selectbox option
  2927.                 //is set, even if the value doesn't change.                
  2928.                 var table = Lazarus.arrayToHashTable(value);
  2929.                 for (var i=0; i<ele.options.length; i++){
  2930.                     var selectOption = table[ele.options[i].value] ? true : false;
  2931.                     if (ele.options[i].selected != selectOption){
  2932.                         ele.options[i].selected = selectOption;
  2933.                     }
  2934.                 }
  2935.             }
  2936.             break;
  2937.             
  2938.         case "iframe":
  2939.             var doc = ele.contentWindow.document;
  2940.             if (doc && doc.body){
  2941.                 doc.body.innerHTML = value;
  2942.             }            
  2943.             break;
  2944.             
  2945.         default:
  2946.             //unknown element type
  2947.             break;
  2948.     }
  2949. }
  2950.  
  2951.  
  2952. /**
  2953. * return a string explaining the type of a form element
  2954. * differentiates between different input types.
  2955. */
  2956. Lazarus.getElementType = function(ele){
  2957.     if (ele.nodeName.toLowerCase() == "input"){
  2958.         return ele.type.toLowerCase();
  2959.     }
  2960.     else {
  2961.         return ele.nodeName.toLowerCase();
  2962.     }
  2963. }
  2964.  
  2965. /**
  2966. * return a fake form object
  2967. */
  2968. Lazarus.createFakeForm = function(ele, type){
  2969.     return {
  2970.         ownerDocument: ele.ownerDocument,
  2971.         elements: [ele],
  2972.         action: Lazarus.urlToDomainId(ele.ownerDocument.URL) || '',
  2973.         isTextarea: (type == "textarea"),
  2974.         isIframe: (type == "iframe"),
  2975.         isFakeForm: true
  2976.     };
  2977. }
  2978.  
  2979. Lazarus.getParentIframe = function(ele){
  2980.     var win = ele.ownerDocument.defaultView.frameElement;
  2981.     var win = doc && doc.defaultView;
  2982.     return win.frameElement;
  2983. }   
  2984.  
  2985.  
  2986. /**
  2987. * return the first content editable iframe or textarea from a given element
  2988. */
  2989. Lazarus.findEditorFromElement = function(ele){
  2990.  
  2991.     while(ele){
  2992.         if (Lazarus.isTextarea(ele)){
  2993.             return ele;
  2994.         }
  2995.         else if (Lazarus.isEditableDoc(ele.ownerDocument) && ele.ownerDocument.defaultView.frameElement){
  2996.             return ele.ownerDocument.defaultView.frameElement; 
  2997.         }
  2998.         else {
  2999.             ele = ele.parentNode;
  3000.         }
  3001.     }
  3002. }
  3003.  
  3004. /**
  3005. * finds a parent form from this element 
  3006. */
  3007. Lazarus.findFormFromElement = function(ele){
  3008.     
  3009.     var iframe = false;
  3010.     
  3011.     while(ele){
  3012.         if (ele.nodeName && ele.nodeName.toLowerCase() == "form"){
  3013.             return ele;
  3014.         }
  3015.         else if (ele.form && ele.form.nodeName.toLowerCase() == "form"){
  3016.             return ele.form;
  3017.         }
  3018.         //add support for AJAX textareas that are not contained within a form.
  3019.         else if (Lazarus.isTextarea(ele)){
  3020.             //we're going to build a fake form here, that contains the , so the rest of the code can handle 
  3021.             return Lazarus.createFakeForm(ele, "textarea");
  3022.         }
  3023.         else if (Lazarus.isEditableDoc(ele.ownerDocument)){
  3024.             iframe = ele.ownerDocument.defaultView.frameElement;
  3025.             //move to parent iframe
  3026.             //Hmmm. Problem exists when the editable iframe is contained within another iframe (eg FCKEditor)
  3027.             //we'll need to iterate up through all the frames. But we also need to check if this frame contains the form element
  3028.             ele = iframe;
  3029.         }
  3030.         else if (ele.parentNode && ele.parentNode.tagName){
  3031.             ele = ele.parentNode;
  3032.         }
  3033.         //an iframe has been detected, but no form was found in this iframe
  3034.         //so move up to the containing frame (if any)
  3035.         else if (iframe){
  3036.             ele = ele.ownerDocument.defaultView.frameElement;
  3037.         }
  3038.         else {
  3039.             return null;
  3040.         }
  3041.     }
  3042.     
  3043.     if (iframe){
  3044.         //we have an editable iframe that is NOT contained within a form!
  3045.         //probably some type of AJAX form
  3046.         //we'll need to build a fake form and the iframe in there
  3047.         return Lazarus.createFakeForm(iframe, "iframe");
  3048.     }
  3049.     
  3050.     return null;
  3051. }
  3052. /**
  3053. * finds the first node of type nodeName from ele up the dom tree
  3054. */
  3055. Lazarus.findParent = function(ele, nodeName){
  3056.     nodeName = nodeName.toLowerCase();
  3057.     while(ele){
  3058.         if (ele.nodeName && ele.nodeName.toLowerCase() == nodeName){
  3059.             return ele;
  3060.         }
  3061.         else {
  3062.             ele = ele.parentNode;
  3063.         }
  3064.     }
  3065.     return null;
  3066. }
  3067.  
  3068. /**
  3069. */
  3070. Lazarus.onStatusbarImageClick = function(evt){
  3071.     
  3072.     var state = Lazarus.getState()
  3073.     
  3074.     if (state == Lazarus.STATE_DISABLED){
  3075.         evt.preventDefault();
  3076.         Lazarus.openGenerateKeysDialog();
  3077.         if (Lazarus.reloadKeys()){
  3078.             Lazarus.enable();
  3079.         }
  3080.         return false;
  3081.     }
  3082.     
  3083.     //only on left click
  3084.     if (evt.which == 1){
  3085.         switch (state){
  3086.             case Lazarus.STATE_ENABLED:
  3087.                 Lazarus.openStatusbarMenu(evt);
  3088.                 return;
  3089.                 
  3090.             case Lazarus.STATE_PASSWORD_REQUIRED:
  3091.                 Lazarus.showEnterPasswordDialog();
  3092.                 return 
  3093.             
  3094.             case Lazarus.STATE_PRIVATE_BROWSING:
  3095.                 alert(Lazarus.getString('private.browsing.mode'));
  3096.                 return;
  3097.             
  3098.             case Lazarus.STATE_GENERATING_KEYS:
  3099.                 return;
  3100.             
  3101.             case Lazarus.STATE_DISABLED_FOR_DOMAIN:
  3102.                 alert(Lazarus.getString('status.disabledfordomain'))
  3103.                 return;
  3104.                 
  3105.             case Lazarus.STATE_UNINITALIZED:
  3106.             default:
  3107.                 Lazarus.error("Not initalized");
  3108.                 return 
  3109.         }
  3110.     }
  3111. }
  3112.  
  3113.  
  3114. /**
  3115. * show the statusbar menu 
  3116. */
  3117. Lazarus.openStatusbarMenu = function(evt){
  3118.     Lazarus.$('lazarus-statusbar-menupopup').openPopup(Lazarus.$('lazarus-statusbarpanel'), 'before_end', 0, 0, true);
  3119. }
  3120.  
  3121. /**
  3122. * open the options dialog
  3123. */
  3124. Lazarus.openOptionsDialog = function(optionsPane){
  3125.     optionsPane = optionsPane || null;
  3126.         
  3127.         var features = "chrome,titlebar,toolbar,centerscreen,modal";
  3128.         features += Lazarus.getPref("browser.preferences.animateFadeIn", false) ? "" : ",resizable";
  3129.         
  3130.     window.open("chrome://lazarus/content/options.xul", "LazarusOptions", features, optionsPane);
  3131. }
  3132.  
  3133. /**
  3134. * open the about dialog
  3135. */
  3136. Lazarus.openAboutDialog = function(){
  3137.     window.open("chrome://lazarus/content/about.xul", "LazarusAbout", "chrome,titlebar,toolbar,centerscreen,modal,resizable");
  3138. }
  3139.  
  3140. /**
  3141. * open the about dialog
  3142. */
  3143. Lazarus.openLazarusWebsite = function(page){
  3144.     page = page || '';
  3145.     Lazarus.openURL(Lazarus.website + page, true, true);
  3146. }
  3147.  
  3148. /**
  3149. * open a URL in the current browser
  3150. */
  3151. Lazarus.openURL = function(url, newTab, selectTab){
  3152.     var browser = document.getElementById('content');
  3153.     
  3154.     if (newTab){
  3155.         var tab = browser.addTab(url);
  3156.         if (tab && selectTab){
  3157.             browser.selectedTab = tab;
  3158.         }    
  3159.     }
  3160.     else {
  3161.         content.location = url;
  3162.     }
  3163. }
  3164.  
  3165.  
  3166. /**
  3167. * returns a localized string 
  3168. */
  3169. Lazarus.getString = function(strId){
  3170.     try {
  3171.         var strbundle = Lazarus.$("lazarus-strings");
  3172.  
  3173.         if (arguments.length == 1){
  3174.             return strbundle.getString(strId);
  3175.         }
  3176.         else {
  3177.             var replacements = [];
  3178.             //NOTE: first argument is the strId
  3179.             for (var i=1; i<arguments.length; i++){
  3180.                 replacements.push(arguments[i])
  3181.             }
  3182.             return strbundle.getFormattedString(strId, replacements);
  3183.         }
  3184.     }
  3185.     catch(e){
  3186.         Lazarus.error(e, "failed to getString ["+ strId +"]");
  3187.         return '';
  3188.     }
  3189. }
  3190.  
  3191.  
  3192. Lazarus.checkDatabase = function(db){
  3193.     //build the database tables
  3194.     try {
  3195.         //forms
  3196.         db.exe('CREATE TABLE IF NOT EXISTS forms (\
  3197.             id INTEGER PRIMARY KEY, formid TEXT, \
  3198.             created INT, \
  3199.             formname TEXT, \
  3200.             forminfo TEXT, \
  3201.             formtext TEXT, \
  3202.             savetype INT, \
  3203.             forminfohash TEXT, \
  3204.             formurl TEXT, \
  3205.             text_length INT, \
  3206.             autofill INT);');
  3207.         db.exe('CREATE INDEX IF NOT EXISTS index_forms_created ON forms (created  DESC)');
  3208.         db.exe('CREATE INDEX IF NOT EXISTS index_forms_formid ON forms (formid  ASC)');
  3209.         
  3210.         //settings
  3211.         db.exe('CREATE TABLE IF NOT EXISTS settings (name TEXT PRIMARY KEY, value TEXT)');
  3212.         
  3213.         //texdata
  3214.         db.exe('CREATE TABLE IF NOT EXISTS textdata (\
  3215.             id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
  3216.             text_encrypted TEXT, \
  3217.             text_hash TEXT, \
  3218.             text_length INT, \
  3219.             summary_encrypted TEXT, \
  3220.             created INTEGER DEFAULT \'0\' NOT NULL, \
  3221.             domain_hash TEXT, \
  3222.             url_encrypted TEXT, \
  3223.             savetype INTEGER \
  3224.             )');
  3225.             
  3226.         Lazarus.debug("DB: Creating indexes");
  3227.             
  3228.         db.exe('CREATE INDEX IF NOT EXISTS index_textdata_domain ON textdata (domain_hash ASC)');
  3229.         db.exe('CREATE INDEX IF NOT EXISTS index_textdata_created ON textdata (created DESC)');
  3230.         
  3231.         Lazarus.debug("DB: Creating Full Text indexes");
  3232.         
  3233.         //textdata full text index
  3234.         if (!db.tableExists('textdata_fulltext')){
  3235.             db.exe('CREATE VIRTUAL TABLE textdata_fulltext USING fts3(hashed_text)');
  3236.         }
  3237.         
  3238.         //forms: full text index
  3239.         if (!db.tableExists('forms_fulltext')){
  3240.             db.exe('CREATE VIRTUAL TABLE forms_fulltext USING fts3(hashed_text)');
  3241.         }
  3242.         
  3243.         Lazarus.debug("DB: Testing read/write");
  3244.         
  3245.         //test read and write 
  3246.         var now = (new Date()).getTime();
  3247.         db.exe("DELETE FROM settings WHERE name = 'last-accessed'");
  3248.         db.exe("INSERT INTO settings (name, value) VALUES ('last-accessed', ?1)", now);
  3249.         var lastAccessed = db.getInt("SELECT value FROM settings WHERE name = 'last-accessed'");
  3250.         var success = (lastAccessed === now);
  3251.         Lazarus.debug("DB: read/write success? "+ success);
  3252.         return success;
  3253.     }
  3254.     catch(e){
  3255.         Lazarus.error(e);
  3256.         return false;
  3257.     }
  3258. }
  3259.  
  3260. /**
  3261. * initalizes the database
  3262. */
  3263. Lazarus.initDB = function(){
  3264.     //create / connect to the database 
  3265.     //we might have already connected in an update script.
  3266.     if (!Lazarus.Global.db){
  3267.     
  3268.         if (Lazarus.getPref("extensions.lazarus.deleteDatabase", false)){ 
  3269.             Lazarus.killPref("extensions.lazarus.deleteDatabase", false);
  3270.             Lazarus.killDB();
  3271.         }
  3272.         
  3273.         if (Lazarus.getPref("extensions.lazarus.restoreDatabase", false)){ 
  3274.             Lazarus.killPref("extensions.lazarus.restoreDatabase");
  3275.             if (Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
  3276.               Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite", true);
  3277.             }
  3278.             else {
  3279.               Lazarus.error("Unable to restore database: backup not found");
  3280.             }
  3281.         }
  3282.     
  3283.         //if a backup exists, and the original database is missing, then try and use the backup database
  3284.         if (Lazarus.file.exists("%profile%/lazarus-backup.sqlite") && !Lazarus.file.exists("%profile%/lazarus.sqlite")){
  3285.           Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
  3286.         }
  3287.         else if (Lazarus.getPref("extensions.lazarus.restoreFromBackup", false) && Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
  3288.           Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
  3289.         }
  3290.         
  3291.         var db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
  3292.         
  3293.         if (!Lazarus.checkDatabase(db)){
  3294.           //save the corrupted database file
  3295.           Lazarus.warning("Database is corrupted, saving corrupt database");
  3296.           var d = Lazarus.formatDate(new Date(), true);
  3297.           Lazarus.file.kill("%profile%/lazarus-"+ d +"-corrupted.sqlite");
  3298.           Lazarus.file.move("%profile%/lazarus.sqlite", "%profile%/lazarus-"+ d +"-corrupted.sqlite");
  3299.           db = null;
  3300.         }
  3301.         
  3302.         //use the backup database if it exists.
  3303.         if (!db && Lazarus.file.exists("%profile%/lazarus-backup.sqlite")){
  3304.           Lazarus.warning("Restoring previous backup database");
  3305.           Lazarus.file.move("%profile%/lazarus-backup.sqlite", "%profile%/lazarus.sqlite");
  3306.           db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
  3307.           if (!Lazarus.checkDatabase(db)){
  3308.             //delete the broken backup file
  3309.             Lazarus.file.kill("%profile%/lazarus.sqlite");
  3310.             db = null;
  3311.           }
  3312.         }
  3313.         
  3314.         if (!db){
  3315.           //delete any existing database, and build a new one from scratch
  3316.           Lazarus.file.kill("%profile%/lazarus.sqlite");
  3317.           db = new Lazarus.SQLite("%profile%/lazarus.sqlite", false);
  3318.           if (!Lazarus.checkDatabase(db)){
  3319.             //oh, so screwed
  3320.             Lazarus.error("Unable to create the Lazarus database");
  3321.             db = null;
  3322.           }
  3323.         }
  3324.         
  3325.         if (db){
  3326.           //ok we should now have a valid and checked database 
  3327.           //make a backup of it.
  3328.           //remove the old backup (if it exists)
  3329.           if (Lazarus.getPref("extensions.lazarus.backupDatabase")){
  3330.             Lazarus.debug("Backing up database");
  3331.             Lazarus.file.copy("%profile%/lazarus.sqlite", "%profile%/lazarus-backup.sqlite", true);
  3332.           }
  3333.         }
  3334.         
  3335.         Lazarus.Global.db = db;
  3336.     }
  3337.     
  3338.     Lazarus.db = Lazarus.Global.db;
  3339.     return Lazarus.db ? true : false;
  3340. }
  3341.  
  3342.  
  3343. Lazarus.cleanDB = function(callback){
  3344.   //takes ages to clean the database
  3345.   Lazarus.debug("Cleaning database");
  3346.   Lazarus.cleaningDatabase = true;
  3347.   Lazarus.refreshIcon();
  3348.   Lazarus.backgroundTask(function(){
  3349.     Lazarus.db.exe("VACUUM");
  3350.   }, function(){
  3351.     Lazarus.cleaningDatabase = false;
  3352.     Lazarus.refreshIcon();
  3353.     if (callback){
  3354.       callback();
  3355.     }
  3356.   });
  3357. }
  3358.  
  3359. /**
  3360. * convert autosaved forms to semi-permanent save points 
  3361. */
  3362. Lazarus.saveAutoSavedForms = function(){
  3363.  
  3364.     //convert autosaved forms to semi-permanent points
  3365.     var rs = Lazarus.db.rs("SELECT id, formid FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_AUTOSAVE +" ORDER BY formid, created DESC");
  3366.     var lastFormId = '';
  3367.     var lastFormCnt = 0;
  3368.     var MAX_AUTOSAVES_TO_KEEP = 2;
  3369.     for (var i=0; i<rs.length; i++){
  3370.         var savedForm = rs[i];
  3371.         if (lastFormId != savedForm["formid"]){
  3372.             lastFormId = savedForm["formid"];
  3373.             lastFormCnt = 1;
  3374.         }
  3375.         else {
  3376.             lastFormCnt++;
  3377.         }
  3378.         
  3379.         if (lastFormCnt <= MAX_AUTOSAVES_TO_KEEP){
  3380.             Lazarus.db.exe('UPDATE forms SET savetype = '+ Lazarus.FORM_TYPE_STALE_AUTOSAVE +' WHERE id = ?1', savedForm["id"]);
  3381.         }
  3382.         else {
  3383.             //excess saved form, remove it
  3384.                         Lazarus.removeForms(savedForm["id"]);
  3385.         }
  3386.     }
  3387. }
  3388.  
  3389. /**
  3390. * removes old/excess forms from the database
  3391. */
  3392. Lazarus.removeOldForms = function(){
  3393.     var rs = Lazarus.db.rs("SELECT count(*) as cnt, formid FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_NORMAL +" GROUP BY formid");
  3394.     var maxForms = Lazarus.getExtPref("maxSavesPerForm", 10);
  3395.     for (var i=0; i<rs.length; i++){
  3396.         var save = rs[i];
  3397.         if (save["cnt"] > maxForms){
  3398.             Lazarus.debug("Removing excess forms "+ save["formid"]);
  3399.             var ids = Lazarus.db.getColumn("SELECT id FROM forms WHERE formid = ?1 AND savetype = "+ Lazarus.FORM_TYPE_NORMAL +" ORDER BY created DESC LIMIT -1 OFFSET ?2", save["formid"], maxForms);
  3400.             if (ids.length){
  3401.                 //and remove
  3402.                 Lazarus.debug("cleaning up forms "+ ids.join(","));
  3403.                                 Lazarus.removeForms(ids);
  3404.             }
  3405.         }
  3406.     }
  3407. }
  3408.  
  3409. /**
  3410. * empties all the tables in the database
  3411. * except those specified.
  3412. */
  3413. Lazarus.emptyDB = function(ignoreFormTypes){
  3414.     //always remove text data
  3415.     Lazarus.db.exe('DELETE FROM textdata');
  3416.     Lazarus.db.exe('DELETE FROM textdata_fulltext');
  3417.     
  3418.     if (typeof ignoreFormTypes == "undefined"){
  3419.         Lazarus.db.exe('DELETE FROM forms');
  3420.         Lazarus.db.exe('DELETE FROM forms_fulltext');
  3421.     }
  3422.     else {
  3423.                 var ids = Lazarus.db.getColumn('SELECT id FROM forms WHERE savetype NOT IN ('+ ignoreFormTypes +')');
  3424.                 Lazarus.removeForms(ids);
  3425.     }
  3426. }
  3427.  
  3428. /**
  3429. * completely remove the database
  3430. */
  3431. Lazarus.killDB = function(){
  3432.     Lazarus.file.kill("%profile%/lazarus.sqlite");
  3433.     Lazarus.file.kill("%profile%/lazarus-backup.sqlite");
  3434. }
  3435.  
  3436. /**
  3437. * run any update functions
  3438. */
  3439. Lazarus.runUpdates = function(prevVersion){
  3440.     for(var key in Lazarus.updates){
  3441.         if (Lazarus.versionCompare(key, prevVersion) == 1){
  3442.             Lazarus.debug("running update "+ key);
  3443.             try {
  3444.                 Lazarus.updates[key](prevVersion);
  3445.             }catch(e){
  3446.                 Lazarus.error(e);
  3447.             }
  3448.         }
  3449.     }
  3450. }
  3451.  
  3452. /**
  3453. * fire a "clear-private-data" event if the user has asked to clear private data on shutdown with no prompt
  3454. */
  3455. Lazarus.fireClearPrivateDataOnShutdown = function(){
  3456.     if (Lazarus.getPref("privacy.sanitize.sanitizeOnShutdown", false) && !Lazarus.getPref("privacy.sanitize.promptOnSanitize", true)){
  3457.         Lazarus.Event.fire("clear-private-data");
  3458.     }
  3459. }
  3460.  
  3461. /**
  3462. * clear private data if all the preferences say so
  3463. */
  3464. Lazarus.onClearPrivateData = function(action){    
  3465.     if (Lazarus.getExtPref("privacy.item.saved.forms")){
  3466.         Lazarus.emptyDB(Lazarus.FORM_TYPE_TEMPLATE);
  3467.         Lazarus.debug("ClearPrivateData: removing all forms");
  3468.     }
  3469. }
  3470.  
  3471. /**
  3472. * return TRUE if lazarus requires a password before data can be decrypted
  3473. */
  3474. Lazarus.isPasswordSet = function(){
  3475.     //we test this by attempting to decrypt the private key with a blank password
  3476.     var encb64Key = Lazarus.db.getStr("SELECT value FROM settings WHERE name = 'private-key'");
  3477.     //we need to unencrypt the private key
  3478.     return Lazarus.Crypto.AESDecrypt(encb64Key, "") ? false : true;
  3479. }
  3480.  
  3481. /*
  3482. * encrypts a string
  3483. */
  3484. Lazarus.encrypt = function(str){
  3485.     return Lazarus.Crypto.encrypt(str);
  3486. }
  3487.  
  3488. /*
  3489. * encrypts a string
  3490. */
  3491. Lazarus.decrypt = function(str){
  3492.     return Lazarus.Crypto.decrypt(str);
  3493. }
  3494.  
  3495.  
  3496. /*
  3497. * encrypts a string
  3498. */
  3499. Lazarus.encrypt2 = function(str){
  3500.     //hmmm interesting, the encrypted string is not always the same.
  3501.     //a certain amount of decryption information must be kept within the encrypted string
  3502.     //so we cannot compare encrypted string against each other to test if they are the same
  3503.     //all encrypted string must be DECRYPTED before comparison.
  3504.     
  3505.     //Unable to decrypt extended characters (eg \u8888)!
  3506.     //IMPORTANT: extended characters are not correctly encoded/decoded in the bsao function used by the encryptor, so (as of 2008-07-24) 
  3507.     //we'll be encoding all strings before encrypting them, and then decoding afterwards.
  3508.     //by adding a 4 character header ("uri:") we can tell which type of string we have (encodeURIComponent).
  3509.     
  3510.     Lazarus.decoderRing = Lazarus.decoderRing || Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
  3511.     var encStr = encodeURIComponent(str);
  3512.     return "uri:"+ Lazarus.decoderRing.encryptString(encStr);
  3513. }
  3514.  
  3515. /**
  3516. * decrypt a string
  3517. */
  3518. Lazarus.decrypt2 = function(str){
  3519.     //IMPORTANT: extended characters (eg \u8888) are not correctly encoded/decoded, so (as of 2008-07-24) 
  3520.     //we'll be encoding all strings before encrypting them, and then decoding afterwards.
  3521.     //by inspecting the 4 character header we can tell which type of string we have.
  3522.     Lazarus.decoderRing = Lazarus.decoderRing || Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
  3523.     if (str.indexOf("uri:") == 0){
  3524.         str = str.substr(4);
  3525.         var strEnc = Lazarus.decoderRing.decryptString(str);
  3526.         return decodeURIComponent(strEnc);
  3527.     }
  3528.     else {
  3529.         return Lazarus.decoderRing.decryptString(str);
  3530.     }
  3531. }
  3532.  
  3533. /**
  3534. * calulate md5 hash of a string
  3535. * ref: http://developer.mozilla.org/en/docs/nsICryptoHash#Computing_the_Hash_of_a_String
  3536. */
  3537. Lazarus.md5 = function(str){
  3538.  
  3539.     var nsICryptoHash = Components.classes['@mozilla.org/security/hash;1'].createInstance(Components.interfaces.nsICryptoHash);
  3540.  
  3541.     var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  3542.     converter.charset = "UTF-8";
  3543.     // result is an out parameter,
  3544.     // result.value will contain the array length
  3545.     var result = {};
  3546.     // data is an array of bytes
  3547.     var data = converter.convertToByteArray(str, result);
  3548.     
  3549.     nsICryptoHash.init(Components.interfaces.nsICryptoHash.MD5);
  3550.     nsICryptoHash.update(data, data.length);
  3551.     var hash = nsICryptoHash.finish(false);
  3552.     
  3553.     // Unpack the binary data bin2hex style
  3554.     var ascii = [];
  3555.     var len = hash.length;
  3556.     for (var i = 0; i < len; ++i) {
  3557.         var c = hash.charCodeAt(i);
  3558.         var ones = c % 16;
  3559.         var tens = c >> 4;
  3560.         ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
  3561.     }
  3562.     return ascii.join('');
  3563. }
  3564.  
  3565.  
  3566. /**
  3567. * return TRUE if master password is set
  3568. */
  3569. Lazarus.isMasterPasswordSet = function(){
  3570.     var secmodDB = Components.classes["@mozilla.org/security/pkcs11moduledb;1"].getService(Components.interfaces.nsIPKCS11ModuleDB);
  3571.     var slot = secmodDB.findSlotByName("");
  3572.     return (slot && (slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN || slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_LOGGED_IN));
  3573.  
  3574. /**
  3575. * return TRUE if a user has set the firefox master password and has not yet logged in.
  3576. */
  3577. Lazarus.isMasterPasswordRequired = function(){
  3578.     var secmodDB = Components.classes["@mozilla.org/security/pkcs11moduledb;1"].getService(Components.interfaces.nsIPKCS11ModuleDB);
  3579.     var slot = secmodDB.findSlotByName("");
  3580.     return (slot && slot.status == Components.interfaces.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN);
  3581. }
  3582.  
  3583. /**
  3584. */
  3585. Lazarus.enterMasterPassword = function(){
  3586.  
  3587.     //encrypting a string should open the enter master password dialog
  3588.     try {
  3589.         var decoderRing = Components.classes["@mozilla.org/security/sdr;1"].getService(Components.interfaces.nsISecretDecoderRing);
  3590.         decoderRing.encryptString("dummy");
  3591.         return true;
  3592.     }
  3593.     catch(e){
  3594.         return false;
  3595.     }
  3596. }
  3597.  
  3598.  
  3599. Lazarus.openGenerateKeysDialog = function(){
  3600.     window.open("chrome://lazarus/content/generate-keys.xul", "LazarusGenerateKeys", "chrome,dialog,modal,resizable,centerscreen");
  3601. }
  3602.  
  3603. /**
  3604. * dictionary of notification to display in the popup.
  3605. */
  3606. Lazarus.notifications = {};
  3607. Lazarus.notifications["password-required"] = {
  3608.     "buttons" : [
  3609.         {
  3610.             "label-string": "notification.password.required.button1.label",
  3611.             "accessKey-string": "notification.password.required.button1.accesskey",
  3612.             "callback": function(notif, label){
  3613.                 Lazarus.showEnterPasswordDialog();
  3614.                 Lazarus.refreshIcon();
  3615.             }
  3616.         }
  3617.     ],
  3618.     "text-string" : "notification.password.required.text",
  3619.     "iconUrl" : "chrome://lazarus/skin/lazarus-error.png"
  3620. }
  3621.  
  3622. Lazarus.notifications["password-required-autofill"] = {
  3623.     "buttons" : [
  3624.         {
  3625.             "label-string": "notification.password.required.autofill.button1.label",
  3626.             "accessKey-string": "notification.password.required.autofill.button1.accesskey",
  3627.             "callback": function(notif, label){
  3628.                 Lazarus.showEnterPasswordDialog();
  3629.                 Lazarus.refreshIcon();
  3630.                 Lazarus.autofillDoc(content.document);  
  3631.             }
  3632.         }
  3633.     ],
  3634.     "text-string" : "notification.password.required.autofill.text",
  3635.     "iconUrl" : "chrome://lazarus/skin/lazarus-error.png"
  3636. }
  3637.  
  3638. Lazarus.notifications["form-restored"] = {
  3639.     "buttons" : [
  3640.         {
  3641.             "label-string": "notification.form.restored.button1.label",
  3642.             "accessKey-string": "notification.form.restored.button1.accesskey",
  3643.             "callback": function(notif, label){ 
  3644.                 
  3645.                 var data = Lazarus.$('lazarus-notification').getAttribute('lazarus-data');
  3646.                 var page = 'donate.html';
  3647.                 page += (data) ? ('?'+ data) : '';
  3648.                 Lazarus.openLazarusWebsite(page);
  3649.             }
  3650.         }
  3651.     ],
  3652.     "text-string" : "notification.form.restored.text",
  3653.     "iconUrl" : "chrome://lazarus/skin/lazarus.png"
  3654. }
  3655.  
  3656. /**
  3657. * displays the lazarus notification box 
  3658. */
  3659. Lazarus.showNotificationBox = function(id, text, data){
  3660.     var notif = Lazarus.notifications[id];
  3661.     
  3662.     if (Lazarus.$('lazarus-notification').currentNotification && Lazarus.$('lazarus-notification').currentNotificationId == id){
  3663.         //do nothing, notification box is already shown
  3664.     }
  3665.     else if (notif){
  3666.         //translate text, and buttons
  3667.         if (text){
  3668.             notif["text"] = text;
  3669.         }
  3670.         else if (!notif["text"] && notif["text-string"]){
  3671.             notif["text"] = Lazarus.getString(notif["text-string"]);
  3672.         }
  3673.         
  3674.         for (var i=0; i<notif.buttons.length; i++){
  3675.             var button = notif.buttons[i];
  3676.             if (!button["label"] && button["label-string"]){
  3677.                 button["label"] = Lazarus.getString(button["label-string"]);
  3678.             }
  3679.             if (!button["accessKey"] && button["accessKey-string"]){
  3680.                 button["accessKey"] = Lazarus.getString(button["accessKey-string"]);
  3681.             }
  3682.         }
  3683.         
  3684.         var notificationBox = Lazarus.$('lazarus-notification');
  3685.         notificationBox.setAttribute('lazarus-data', data || '');
  3686.         notificationBox.currentNotificationId = id;
  3687.         notificationBox.appendNotification(notif["text"], id, notif["iconUrl"], notificationBox.PRIORITY_INFO_LOW, notif["buttons"]);
  3688.     }
  3689.     else {
  3690.         Lazarus.error(Error("Lazarus: invalid notification id ["+ id +"]"));
  3691.     }
  3692. }
  3693.  
  3694. /**
  3695. * closes the notification box
  3696. */
  3697. Lazarus.closeNotificationBox = function(immediate){
  3698.     Lazarus.$('lazarus-notification').removeAllNotifications(immediate);
  3699. }
  3700.  
  3701. /**
  3702. * returns a list of the templates found in the database
  3703. */
  3704. Lazarus.getTemplateNames = function(){
  3705.     return Lazarus.db.getColumn("SELECT formname FROM forms WHERE savetype = "+ Lazarus.FORM_TYPE_TEMPLATE +" ORDER BY formname");    
  3706. }
  3707.  
  3708. /**
  3709. * handle user clicking on the "Save form as template..." menuitem
  3710. */
  3711. Lazarus.onSaveFormMenuItem = function(){
  3712.     //find the current form
  3713.     var form = Lazarus.findFormFromElement(gContextMenu.target, "form");
  3714.     
  3715.     //and show the save as template dialog
  3716.     if (form){
  3717.         var info = Lazarus.formInfo(form, Lazarus.FORM_TYPE_NORMAL);
  3718.         var args = {};
  3719.         args.templateName = '';
  3720.         //add the names of the current templates within the database
  3721.         args.templateNames = Lazarus.getTemplateNames();
  3722.         //suggest using the default name for this form
  3723.         args.defaultName = Lazarus.getMenuItemText(info.formtext) || ("["+ Lazarus.getString("untitled") +"]");
  3724.         //WYSIWYG and TEXTAREAs cannot be autofilled
  3725.         args.isTextarea = form.isTextarea;
  3726.         args.isIframe = form.isIframe;
  3727.         
  3728.         //IMPORTANT: must use open dialog here otherwise the arguments are not passed.
  3729.         window.openDialog("chrome://lazarus/content/template-save.xul", "LazarusTemplateSave", "chrome,modal,resizable", args);
  3730.         if (args.templateName){
  3731.             Lazarus.saveForm(form, Lazarus.FORM_TYPE_TEMPLATE, args.templateName, args.autofill);
  3732.         }
  3733.     }
  3734.     //or throw an error
  3735.     else {
  3736.         alert(Lazarus.getString("error.form.object.not.found"));
  3737.         Lazarus.error(Error("Unable to find form in the document"));    
  3738.     }
  3739. }
  3740.  
  3741. /**
  3742. * compare two version numbers (eg 2.0.0.1beta3, 2.0.0rc1)
  3743. * return 1 if version1 > version2
  3744. * return 0 if version1 == version2
  3745. * return -1 if version1 < version2
  3746. */
  3747. Lazarus.versionCompare = function(version1, version2){
  3748.  
  3749.     function getValueOfVersionSegment(seg){
  3750.     
  3751.         if (typeof seg === "undefined"){
  3752.             return 0;
  3753.         }
  3754.         else if (/^\d+$/.test(seg)){
  3755.             return parseInt(seg);
  3756.         }
  3757.         else {
  3758.             switch(seg){
  3759.                 case "rc": return -1;
  3760.                 case "beta": return -2;
  3761.                 case "alpha": return -3;
  3762.                 case "dev": return -4;
  3763.                 default:
  3764.                     throw Error("Lazarus.versionCompare: Unknown version fragment ["+ seg +"]");
  3765.             }
  3766.         }
  3767.     }
  3768.  
  3769.     //verify version strings
  3770.     var regexVerify = /^[\d\.(dev|alpha|beta|rc)]+$/i;
  3771.     
  3772.     if (!regexVerify.test(version1)){
  3773.         Lazarus.error("Lazarus.versionCompare: Invalid version string ["+ version1 +"]");
  3774.         version1 = "0";
  3775.     }
  3776.     else if (!regexVerify.test(version2)){
  3777.         Lazarus.error("Lazarus.versionCompare: Invalid version string ["+ version2 +"]");
  3778.         version2 = "0";
  3779.     }
  3780.     
  3781.     //split each version into sections 
  3782.     var ver1 = version1.toLowerCase().replace(/(\w)(\d)/g, "$1.$2").replace(/(\d)(\w)/g, "$1.$2").split(/[\.\b]/g);
  3783.     var ver2 = version2.toLowerCase().replace(/(\w)(\d)/g, "$1.$2").replace(/(\d)(\w)/g, "$1.$2").split(/[\.\b]/g);
  3784.     
  3785.     //compare each section until a non match occurs
  3786.     var maxLen = Math.max(ver1.length, ver2.length);
  3787.     for (var i=0; i<maxLen; i++){
  3788.         //convert the version segment into a numeric value
  3789.         var seg1 = (typeof ver1[i] === "undefined") ? 0 : getValueOfVersionSegment(ver1[i]);
  3790.         var seg2 = (typeof ver2[i] === "undefined") ? 0 : getValueOfVersionSegment(ver2[i]);
  3791.         
  3792.         if (seg1 > seg2){
  3793.             return 1;
  3794.         }
  3795.         else if (seg1 < seg2){
  3796.             return -1;
  3797.         }            
  3798.     }
  3799.     //all parts are equal
  3800.     return 0;
  3801. }
  3802.  
  3803.  
  3804. /**
  3805. * return the path to this extension install directory
  3806. * gives correct path even if extension is not installed in a users profile directory.
  3807. */
  3808. Lazarus.getExtensionDir = function(extId){
  3809.     // the extension's id from install.rdf
  3810.     extId = extId || Lazarus.guid;
  3811.     var em = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
  3812.     return em.getInstallLocation(extId).getItemFile(extId, "install.rdf").parent.path;
  3813. }
  3814.  
  3815. /**
  3816. * check the website to see if there is a new update available
  3817. */
  3818. Lazarus.checkForUpdates = function(){
  3819.     //never check more than once a day
  3820.     if (Lazarus.getExtPref("lastUpdateCheck") + (24 * 60 * 60) < Lazarus.timestamp()){
  3821.         Lazarus.setExtPref("lastUpdateCheck", Lazarus.timestamp());
  3822.         
  3823.         // create browser
  3824.         var browser = document.createElement("browser");
  3825.         browser.collapsed = true;
  3826.         //browser.style.height = "0px";
  3827.         document.documentElement.appendChild(browser);
  3828.         
  3829.         // set restrictions as needed
  3830.         browser.webNavigation.allowAuth = false;
  3831.         browser.webNavigation.allowImages = true;
  3832.         browser.webNavigation.allowJavascript = true; // so the google analytic code works
  3833.         browser.webNavigation.allowMetaRedirects = false;
  3834.         browser.webNavigation.allowPlugins = false;
  3835.         browser.webNavigation.allowSubframes = false;
  3836.         
  3837.         // listen for load
  3838.         browser.addEventListener("DOMContentReady", function(evt){
  3839.             // the document of the HTML in the DOM
  3840.             var doc = evt.originalTarget;
  3841.             //check URL (could be about:blank)
  3842.             if (doc && doc.URL != "about:blank"){
  3843.                 var ele = doc.getElementById('latest-stable');
  3844.                 if (ele && ele.innerHTML){
  3845.                     Lazarus.setExtPref("lastestStableVersion", ele.innerHTML);     
  3846.                 }
  3847.                 var ele = doc.getElementById('latest-beta');
  3848.                 if (ele && ele.innerHTML){
  3849.                     Lazarus.setExtPref("lastestBetaVersion", ele.innerHTML);     
  3850.                 }
  3851.                 // remove browser element when done
  3852.                 //mouse pointer has loading symbol unless we do this?
  3853.                 browser.contentDocument.location = "about:blank";
  3854.                 browser.parentNode.removeChild(browser);
  3855.                 
  3856.                 Lazarus.updateVersionIcons();
  3857.             }
  3858.         }, true);
  3859.         
  3860.         // load update check
  3861.         browser.contentDocument.location.href = "http://lazarus.interclue.com/version-check.php";   
  3862.  
  3863.         //check again tomorrow
  3864.         setTimeout(Lazarus.checkForUpdates, 24 * 60 * 60 * 1000);
  3865.     }
  3866.     else {
  3867.         //didn't check, so check again in 10 minutes
  3868.         setTimeout(Lazarus.checkForUpdates, 10 * 60 * 1000);
  3869.     }
  3870. }
  3871.  
  3872. /**
  3873. * displays the "new beta" or "new stable" icons is a newer version is available.
  3874. */
  3875. Lazarus.updateVersionIcons = function(){
  3876.     var stable = Lazarus.getExtPref("lastestStableVersion", "");
  3877.     var beta = Lazarus.getExtPref("lastestBetaVersion", "");
  3878.     var thisVersion = Lazarus.getVersionStr();
  3879.     if (beta && Lazarus.getExtPref("checkForUpdatesBeta") && Lazarus.versionCompare(thisVersion, beta) == -1){
  3880.         //show the new beta icon
  3881.         Lazarus.$("lazarus-update-beta-statusbar-image").hidden = false;
  3882.     }
  3883.     else if (stable && Lazarus.getExtPref("checkForUpdates") && Lazarus.versionCompare(thisVersion, stable) == -1){
  3884.         //show the new stable version icon
  3885.         Lazarus.$("lazarus-update-stable-statusbar-image").hidden = false;
  3886.     }
  3887. }
  3888.  
  3889. /**
  3890. * attempts to download the latest versin of Lazarus
  3891. */
  3892. Lazarus.getUpdate = function(version){
  3893.     var urls = {
  3894.         "stable": "http://lazarus.interclue.com/downloads/lazarus.xpi",
  3895.         "beta" : "http://lazarus.interclue.com/downloads/lazarus-beta.xpi"
  3896.     }
  3897.     
  3898.     var url = urls[version] ? urls[version] : urls["stable"];
  3899.     Lazarus.getBrowser().content.location = url;
  3900. }
  3901.  
  3902. /**
  3903. * return TRUE if the user is in private Browsing mode (Fx 3.1+)
  3904. */
  3905. Lazarus.isPrivateBrowsing = function(){
  3906.  
  3907.     try {
  3908.         return Components.classes["@mozilla.org/privatebrowsing;1"] && Components.classes["@mozilla.org/privatebrowsing;1"].getService(Components.interfaces.nsIPrivateBrowsingService).privateBrowsingEnabled;
  3909.     }
  3910.     catch(e){
  3911.         return false;
  3912.     }
  3913. }
  3914.  
  3915. /**
  3916. * return TRUE if Lazarus is currently disabled because the user is in private browsing mode
  3917. */
  3918. Lazarus.isDisabledByPrivateBrowsing = function(){
  3919.     return (Lazarus.isPrivateBrowsing() && !Lazarus.getPref("extensions.lazarus.enableInPrivateBrowsingMode"));
  3920. }
  3921.  
  3922. /**
  3923. * return an FNV1a hash of the given string
  3924. */
  3925. Lazarus.FNV1a = function(str, seed){
  3926.     
  3927.     //hash = offset_basis
  3928.     var hash = seed ? parseInt(seed, 16) : 2166136261;
  3929.     
  3930.     //only calculate the length once
  3931.     var len = str.length;
  3932.     
  3933.     //for each octet_of_data to be hashed
  3934.     for (var i=0; i<len; i++){
  3935.     
  3936.         hash ^= str.charCodeAt(i);
  3937.         
  3938.         //hash = hash * FNV_prime (apparently this bitshifting does the same thing)
  3939.         hash += ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24));
  3940.     }
  3941.     
  3942.     return Math.abs(Number(hash & 0x00000000ffffffff)).toString(16);
  3943. }
  3944.  
  3945.  
  3946. Lazarus.openTextManager = function(){
  3947.  
  3948.     //if not logged in close window
  3949.     if (!Lazarus.canDecrypt()){
  3950.         //try loggin in immediately
  3951.         Lazarus.showEnterPasswordDialog();
  3952.     }
  3953.     
  3954.     //if they still can't login do nothing
  3955.     if (Lazarus.canDecrypt()){
  3956.         window.open("chrome://lazarus/content/text-manager.xul", "", "chrome,centerscreen,resizable");
  3957.     }
  3958. }
  3959.  
  3960. /**
  3961. *
  3962. **/
  3963. Lazarus.refreshMenuIcons = function(){
  3964.  
  3965.     var items = {
  3966.         'lazarus-enterpassword-contextmenuitem': 'chrome://lazarus/skin/lazarus-login.png',
  3967.         'lazarus-restoretext-submenu': 'chrome://lazarus/skin/lazarus.png',
  3968.         'lazarus-restoretextdisabled-contextmenuitem': 'chrome://lazarus/skin/lazarus-disable.png',
  3969.         'lazarus-restoreform-contextmenuitem': 'chrome://lazarus/skin/lazarus.png',
  3970.         'lazarus-restoreform-submenu': 'chrome://lazarus/skin/lazarus.png',
  3971.         'lazarus-submenu-menuitem-donate': 'chrome://lazarus/skin/donate.png',
  3972.         'lazarus-saveform-contextmenuitem': 'chrome://lazarus/skin/lazarus-save.png',
  3973.         'lazarus-domaindisabled-contextmenuitem': 'chrome://lazarus/skin/lazarus-disabled.png',
  3974.         'lazarus-privatebrowsing-contextmenuitem': 'chrome://lazarus/skin/lazarus-disabled.png'
  3975.     };
  3976.     
  3977.     var show = Lazarus.getPref('extensions.lazarus.showContextMenuIcons');
  3978.     
  3979.     for(var id in items){
  3980.         Lazarus.setMenuIcon(document.getElementById(id), show ? items[id] : null);
  3981.     }
  3982. }
  3983.  
  3984.  
  3985. /**
  3986. */
  3987. Lazarus.setMenuIcon = function(ele, iconURL){
  3988.     var classname = ele.tagName.toLowerCase() +"-iconic";
  3989.     
  3990.     if (iconURL){
  3991.         ele.setAttribute("image", iconURL);
  3992.         Lazarus.addClass(ele, classname);
  3993.     }
  3994.     else {
  3995.         //remove the icon
  3996.         ele.setAttribute("image", "");
  3997.         Lazarus.removeClass(ele, classname);
  3998.     }
  3999. }
  4000.  
  4001.  
  4002. /**
  4003. * return TRUE if class exists 
  4004. */
  4005. Lazarus.classExists = function(ele, classname){
  4006.     var currName = ele.getAttribute('class') || '';
  4007.     return ((" "+ currName.toLowerCase() +" ").indexOf(" "+ classname.toLowerCase() +" ") > -1);
  4008. }
  4009.  
  4010.  
  4011. /**
  4012. * adds a classname to an element if the name doesn't exist
  4013. */
  4014. Lazarus.addClass = function(ele, classname){
  4015.     if (!Lazarus.classExists(ele, classname)){
  4016.         ele.setAttribute('class', (ele.getAttribute('class') || '') +" "+ classname);
  4017.     }
  4018. }
  4019.  
  4020.  
  4021. /**
  4022. * remove a classname from an element
  4023. */
  4024. Lazarus.removeClass = function(ele, classname){
  4025.     if (Lazarus.classExists(ele, classname)){
  4026.         var newClassname = ele.getAttribute('class').toLowerCase().replace(classname.toLowerCase(), "").replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
  4027.         ele.setAttribute('class', newClassname);
  4028.     }
  4029. }
  4030.  
  4031. /**
  4032. */
  4033. Lazarus.test = function(evt){
  4034.  
  4035.  
  4036.     Lazarus.cleanDB();
  4037.     return;
  4038.     
  4039.     
  4040.     alert(1);
  4041.     Lazarus.backgroundTask(function(){
  4042.     
  4043.       var x = 1234
  4044.       for(var i=0; i<20000000; i++){
  4045.         x = x/100;
  4046.       } 
  4047.       
  4048.       //try throwing an error in the middle of the function
  4049.       x = a.b.c;
  4050.       
  4051.       return x;
  4052.       
  4053.     
  4054.     }, function(result){
  4055.       alert(result);
  4056.     })
  4057.  
  4058.     
  4059.     return;
  4060.     
  4061.  
  4062.     alert("Private = "+ Lazarus.isPrivateBrowsing());
  4063.     return;
  4064.     
  4065.  
  4066.     
  4067. //~ Unit.assert("Lazarus.canEncrypt()", true, "should be able to encrypt");
  4068.  
  4069. //~ alert(Unit.getLog());
  4070. //~ return;
  4071.  
  4072.  
  4073.     var len = 10;
  4074.  
  4075.     var p = new Profiler("decrypt");
  4076.     var enc = [];
  4077.     var dec = [];
  4078.     for (var i=0; i<len; i++){
  4079.         enc[i] = Lazarus.encrypt("abc"+ Math.random());
  4080.     }
  4081.     p.mark("encrypted "+ len +" strings");
  4082.     for (var i=0; i<len; i++){
  4083.         dec[i] = Lazarus.decrypt(enc[i]);
  4084.     }
  4085.     
  4086.     debug(p.stop("decrypted"));
  4087.     return;
  4088.     
  4089.     
  4090.  
  4091.  
  4092.     debug(Lazarus.db.rs("SELECT hashed_text FROM textdata_fulltext LIMIT 1"));
  4093.     return;
  4094.  
  4095.     Lazarus.openTextManager();
  4096.     return;
  4097.  
  4098.     var str = prompt("Enter search string");
  4099.     if (str){
  4100.         debug(str, Lazarus.hashQuery(str));
  4101.         var rs = Lazarus.db.rs("SELECT id, text_encrypted FROM textdata JOIN textdata_fulltext ON textdata.id = textdata_fulltext.docid WHERE textdata_fulltext.hashed_text MATCH ?1", Lazarus.hashQuery(str));
  4102.         debug(rs);
  4103.         
  4104.         var text = '';
  4105.         for (var i=0; i<rs.length; i++){
  4106.             text += rs[i].id +":"+ Lazarus.decrypt(rs[i].text_encrypted) +"\n";
  4107.         }
  4108.         alert(text ? text : "== no results ==");
  4109.     }
  4110.     return;
  4111.     
  4112.  
  4113.  
  4114.     alert(Lazarus.hash("chongo <Landon Curt Noll> /\\../\\"));
  4115.     return;
  4116.  
  4117.  
  4118.     var p = new Profiler('encrypt large file');
  4119.     var s = Lazarus.file.readBinary("%profile%/test.sqlite");
  4120.     p.mark("Read file");
  4121.     var enc = Lazarus.encrypt(s);
  4122.     p.mark("Encrypt file");
  4123.     var dec = Lazarus.decrypt(enc);
  4124.     p.mark("Decrypt file : "+ (dec == s));
  4125.     
  4126.     //Lazarus.file.readBinary("%profile%/test.sqlite");
  4127.     alert(p.stop("finished"));
  4128.     return;
  4129.  
  4130.  
  4131.  
  4132.     alert(Lazarus.enterMasterPassword());
  4133.     return;
  4134.  
  4135.     alert(1);
  4136.     Lazarus.isMasterPasswordSet();
  4137.     alert(2);
  4138.     
  4139.     
  4140.     Lazarus.isMasterPasswordSet();
  4141.     return;
  4142.  
  4143.  
  4144.     Lazarus.db.exe("DELETE FROM settings WHERE name = 'public-key' OR name = 'private-key'");
  4145.     return;
  4146.     
  4147.     
  4148.     var p = new Profiler("encryption");
  4149.     
  4150.     var str = "{testing...}";
  4151.     
  4152.     var str = '';
  4153.     for (var i=0; i<200000; i++){
  4154.         str += "A";
  4155.     }
  4156.     
  4157.     var enc = Lazarus.Crypto.encrypt(str);
  4158.     debug(enc);
  4159.     p.mark("Encrypted string (Hybrid) of length "+ str.length);
  4160.     
  4161.     var dec = Lazarus.Crypto.decrypt(enc);
  4162.     p.mark("Decrypted string (Hybrid): "+ ((dec == str) ? "success" : "FAILED!"));
  4163.     
  4164.     //compare with SSD encryption
  4165.     var enc = Lazarus.encrypt(str);
  4166.     p.mark("Encrypted string (SSD) of length "+ str.length);
  4167.     
  4168.     var dec = Lazarus.decrypt(enc);
  4169.     
  4170.     alert(p.stop("Decrypted string (SSD): "+ ((dec == str) ? "success" : "FAILED!")));
  4171.     return;
  4172.     
  4173.     
  4174.     var keyPair = Lazarus.Crypto.generateRSAKeyPair("{password}");
  4175.     
  4176.     p.mark("Generate RSA key pair");
  4177.     
  4178.     var symKey = Lazarus.Crypto.crypto.generateRandomKey();
  4179.     
  4180.     //attempting to amke a smaller symkey
  4181.     symKey = btoa(atob(symKey).substr(0, 16));
  4182.     
  4183.     p.mark("Generate symetric key ["+ atob(symKey).length +" characters]");
  4184.     
  4185.     var wrapped = Lazarus.Crypto.crypto.wrapSymmetricKey(symKey, keyPair.pubkey);
  4186.     
  4187.     p.mark("RSA Encrypt symetric key");
  4188.                                               
  4189.     var unwrapped = Lazarus.Crypto.crypto.unwrapSymmetricKey(wrapped, keyPair.privkey,
  4190.                                               keyPair.password,
  4191.                                               keyPair.passphraseSalt,
  4192.                                               keyPair.privkeyWrapIV);
  4193.     
  4194.     
  4195.     p.mark("RSA Decrypt symetric key");
  4196.     
  4197.     debug(unwrapped, symKey == unwrapped);
  4198.     
  4199.     alert(p.stop(symKey == unwrapped ? "success" : "failed!"));
  4200.     
  4201.     
  4202.     return;
  4203.     
  4204.     
  4205.     var str = '';
  4206.     for (var i=0; i<200000; i++){
  4207.         str += "A";
  4208.     }
  4209.     
  4210.     
  4211.     str = "a very short string.";
  4212.     
  4213.     //speed test
  4214.     var p = new Profiler("Encrypting a str of length :"+ str.length);
  4215.  
  4216.     var keypair = Lazarus.Crypto.generateKeyPair(512);
  4217.     
  4218.     p.mark("generate RSA key pair (512 bit)");
  4219.     
  4220.     
  4221.     debug(Lazarus.Crypto.crypto);
  4222.     
  4223.     var enc = Lazarus.Crypto.encrypt(str, keypair.public);
  4224.     p.mark("hybrid encrypt a string");
  4225.     
  4226.     
  4227.     var dec = Lazarus.Crypto.decrypt(enc, keypair.private);
  4228.     debug(dec);
  4229.     
  4230.     
  4231.     p.mark("hybrid decrypt a string"+ ((str == dec) ? "success" : "FAILED!"));
  4232.     
  4233.     alert(p.stop());
  4234.     //var enc = Lazarus.Crypto.encrypt("a string \u5555");
  4235.     
  4236.     //debug(atob(Lazarus.Crypto.crypto.generateRandomIV()));
  4237.     //debug(atob(Lazarus.Crypto.crypto.generateRandomKey()));
  4238.     
  4239.     return;
  4240.     
  4241.     
  4242.     //labs.mozilla.com/Weave/Crypto
  4243.     //interclue.com/Lazarus/Crypto2
  4244.     
  4245.     try {
  4246.         var xx = Components.classes["@labs.mozilla.com/Weave/Crypto;1"].
  4247.             createInstance(Components.interfaces.IWeaveCrypto);
  4248.     }
  4249.     catch(e){
  4250.         if (confirm("Failed to create Weave:crypto object, weave not installed?\nInstall Weave now?")){
  4251.             Lazarus.openURL('http://dev.izeal.com/xpi/lazarus/weave-0.2.98.xpi', true, true);
  4252.         }
  4253.     }
  4254.     
  4255.     
  4256.     debug(xx);
  4257.     return;
  4258.     
  4259.     
  4260.     var randIV = xx.generateRandomIV();
  4261.     //debug(randIV);
  4262.     
  4263.     var randKey = xx.generateRandomKey();
  4264.     //debug(randKey);
  4265.     
  4266.     var str = '';
  4267.     for (var i=0; i<200000; i++){
  4268.         str += "A";
  4269.     }
  4270.     
  4271.     //speed test
  4272.     var p = new Profiler("Encrypting a str of length :"+ str.length);
  4273.     var enc = xx.encrypt(str, randKey, randIV);
  4274.     p.mark("weave (aes-256-cbc): encrypted");
  4275.     var dec = xx.decrypt(enc, randKey, randIV);
  4276.     p.mark("weave (aes-256-cbc): decrypted : "+ ((str == dec) ? "success" : "FAILED!"));
  4277.     var enc = Lazarus.encrypt(str);
  4278.     p.mark("SecretKeyRing: encrypted");
  4279.     var dec = Lazarus.decrypt(enc);
  4280.     p.mark("SecretKeyRing: decrypted : "+ ((str == dec) ? "success" : "FAILED!"));
  4281.     
  4282.     alert(p.stop());
  4283.     
  4284.     
  4285.     //alert("Private = "+ Lazarus.isPrivateBrowsing());
  4286.     
  4287.     return;
  4288.       //~ var pbs = Components.classes["@mozilla.org/privatebrowsing;1"].getService(Components.interfaces.nsIPrivateBrowsingService);
  4289.       
  4290.       //~ // are we currently in the Private Browsing mode?
  4291.       //~ var inPrivateBrowsingMode = pbs.privateBrowsingEnabled;
  4292.         //~ alert(inPrivateBrowsingMode);
  4293.         //~ return;
  4294.         
  4295.     
  4296.     
  4297.     //~ var pbs = Components.classes["@mozilla.org/browser/privatebrowsing;1"]
  4298.                     //~ .getService(Components.interfaces.nsIPrivateBrowsingService);
  4299.                     
  4300.     //~ pbs.privateBrowsing = !pbs.privateBrowsing;
  4301.     //~ alert(pbs.privateBrowsing);
  4302.  
  4303.     //~ return;
  4304.  
  4305.  
  4306.     //http://lazarus.interclue.com/
  4307.     var url = "http://tinyurl.com/5qhvvw";
  4308.     var log = [];
  4309.     var xmlhttp = new XMLHttpRequest();
  4310.     xmlhttp.open("HEAD", url);
  4311.     xmlhttp.onreadystatechange = function(){
  4312.         try {
  4313.             //debug(xmlhttp.readyState);
  4314.             //debug(xmlhttp.status);
  4315.             //debug(xmlhttp.getAllResponseHeaders());
  4316.         }catch(e){}
  4317.         
  4318.         if (xmlhttp.readyState == 4){
  4319.             Lazarus.dump(xmlhttp.channel.originalURI);
  4320.             Lazarus.dump(xmlhttp.channel.URI);
  4321.         }
  4322.         
  4323.     }
  4324.     xmlhttp.send(null);
  4325.     return;
  4326.     
  4327.     //Lazarus.checkForUpdates();
  4328.     return;
  4329.  
  4330.     alert(decodeURIComponent(encodeURIComponent("\u2663")))
  4331.  
  4332.     var str = "\u2663";
  4333.     alert(str +" ["+ str.length +"]");
  4334.     var enc = Lazarus.encrypt(str); 
  4335.     alert(enc);
  4336.     var s = Lazarus.decrypt(enc);
  4337.     alert(s);
  4338.     //~ var alertsService = Components.classes["@mozilla.org/alerts-service;1"]
  4339.                               //~ .getService(Components.interfaces.nsIAlertsService);
  4340. //~ alertsService.showAlertNotification("chrome://mozapps/skin/downloads/downloadIcon.png", 
  4341.                                     //~ "Alert title", "Alert text goes here.", 
  4342.                                     //~ false, "", null);
  4343.  
  4344. //~ function setToNormalZ(aWindow) {
  4345.      //~ aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  4346.      //~ var webnav = aWindow.getInterface(Components.interfaces.nsIWebNavigation);
  4347.      //~ var dsti = webnav.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
  4348.      //~ var treeowner = dsti.treeOwner;
  4349.      //~ var ifreq = treeowner.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  4350.      //~ var xulwin = ifreq.getInterface(Components.interfaces.nsIXULWindow);
  4351.      //~ xulwin.zLevel = 9;
  4352. //~ }
  4353.  
  4354.  
  4355.   //~ var win = window.open("chrome://lazarus/content/test.xul", "test", "chrome");
  4356.  
  4357.  
  4358. //~ setTimeout(function(){
  4359.     //~ //
  4360.     //~ //alert("a");
  4361.     //~ setToNormalZ(win);
  4362.  
  4363. //~ }, 3000);
  4364.  
  4365. //~ <?xml-stylesheet href="chrome://global/skin/alerts/alert.css" type="text/css"?>
  4366.  
  4367. //~ <window id="alertNotification"
  4368.         //~ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  4369.         //~ windowtype="alert:alert"
  4370.         //~ xmlns:xhtml2="http://www.w3.org/TR/xhtml2"
  4371.         //~ xmlns:wairole="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#"
  4372.         //~ xhtml2:role="wairole:alert"
  4373.         //~ align="start"
  4374.         //~ onload="onAlertLoad()">
  4375.         
  4376.     
  4377.     //~ if (Lazarus.state == Lazarus.STATE_DISABLED){
  4378.         //~ Lazarus.enable();
  4379.     //~ }
  4380.     //~ else {
  4381.         //~ Lazarus.disable();
  4382.     //~ }
  4383.     
  4384.     //Lazarus.warning("test");
  4385.     //Lazarus.updates["1.0.1beta4"]();
  4386.     
  4387.     //~ Lazarus.debug(Lazarus.getBaseDomain('www.127.0.0.1'));
  4388.     //~ Lazarus.debug(Lazarus.getBaseDomain('domain.tv.com'));
  4389.     //~ Lazarus.debug(Lazarus.getBaseDomain('domain.com.tv'));
  4390.     //~ Lazarus.debug(Lazarus.getBaseDomain('domain.tv'));
  4391.     //~ Lazarus.debug(Lazarus.getBaseDomain('domain.com'));
  4392.     //~ Lazarus.debug(Lazarus.getBaseDomain('secure.email.website.co.uk'));
  4393.     //~ Lazarus.debug(Lazarus.getBaseDomain('this.is.a.worst.shortly.subdomain.thisIsMyMainWebsite.com.cl'));
  4394. }
  4395.  
  4396.  
  4397.  
  4398.  
  4399. Lazarus.icon = "data:image/png;base64,\
  4400. iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAALHRFWHRDcmVhdGlvbiBUaW1lAFRo\
  4401. dSAxMiBOb3YgMjAwOSAxMzoxOToxNiArMTIwMJCaV2oAAAAHdElNRQfZCwwAFimayURVAAAACXBI\
  4402. WXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAASZJREFUeNq1ks1KAmEUhh+/GZkGs6IS\
  4403. gzYhhREEwbRw56IbSaIbaNe+K2gvFEWB11Cgm4LUKKGfRRqVpKVTpkJpOpOJow6ufVfn8D7nfC8f\
  4404. B4YlR39zesHc8wPrRpNx9xildJrw9hZZyxdWkUggV3XWLhPcn8XZcQoyX2W0/mVdODPSesWBojh5\
  4405. D+/yYTp4q+g93wZ//mA6Fc4XFlnaO8L38sS8XuIuEukxslWsSISaE3xLPvzVCv56g9rGJpVcltWW\
  4406. fWiDYzHU/Ct4vDy6RwlcX3Fyk4Ji0f4JbWlae1CKpwjsH3MQCrHciSkGMieTNEwTwzSYUhQmJRVv\
  4407. xzIG4H9Fo4haHa8QuFwqs8GgPYLc3xQ8iN9bpvM51GqZmUKhvaw5tJPo6g+jvlxgBqGatwAAAABJ\
  4408. RU5ErkJggg==";